From e3ca68edaf7b34ee17afe13f2117fed87c200007 Mon Sep 17 00:00:00 2001 From: Ludovico Fischer Date: Thu, 30 Dec 2021 18:58:46 +0100 Subject: [PATCH 01/43] [Fix] `named`/`ExportMap`: 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. --- CHANGELOG.md | 3 +++ src/ExportMap.js | 10 +++++++++- src/rules/named.js | 3 ++- tests/files/dynamic-import-in-commonjs.js | 5 +++++ tests/src/rules/named.js | 11 ++++++++++- 5 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 tests/files/dynamic-import-in-commonjs.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 88d7c411f7..cf11927c7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - `importType`: avoid crashing on a non-string' ([#2305], thanks [@ljharb]) - [`first`]: prevent crash when parsing angular templates ([#2210], thanks [@ljharb]) - `importType`: properly resolve `@/*`-aliased imports as internal ([#2334], thanks [@ombene]) +- [`named`]/`ExportMap`: handle named imports from CJS modules that use dynamic import ([#2341], thanks [@ludofischer]) ### Changed - [`no-default-import`]: report on the token "default" instead of the entire node ([#2299], thanks [@pmcelhaney]) @@ -951,6 +952,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2341]: https://github.com/import-js/eslint-plugin-import/pull/2341 [#2334]: https://github.com/import-js/eslint-plugin-import/pull/2334 [#2305]: https://github.com/import-js/eslint-plugin-import/pull/2305 [#2299]: https://github.com/import-js/eslint-plugin-import/pull/2299 @@ -1549,6 +1551,7 @@ for info on changes for earlier releases. [@lo1tuma]: https://github.com/lo1tuma [@loganfsmyth]: https://github.com/loganfsmyth [@luczsoma]: https://github.com/luczsoma +[@ludofischer]: https://github.com/ludofischer [@lukeapage]: https://github.com/lukeapage [@lydell]: https://github.com/lydell [@Mairu]: https://github.com/Mairu diff --git a/src/ExportMap.js b/src/ExportMap.js index d818fa6ca8..564b5d63d8 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -43,6 +43,10 @@ export default class ExportMap { */ this.imports = new Map(); this.errors = []; + /** + * type {'ambiguous' | 'Module' | 'Script'} + */ + this.parseGoal = 'ambiguous'; } get hasDefault() { return this.get('default') != null; } // stronger than this.has @@ -406,7 +410,8 @@ ExportMap.parse = function (path, content, context) { }, }); - if (!unambiguous.isModule(ast) && !hasDynamicImports) return null; + const unambiguouslyESM = unambiguous.isModule(ast); + if (!unambiguouslyESM && !hasDynamicImports) return null; const docstyle = (context.settings && context.settings['import/docstyle']) || ['jsdoc']; const docStyleParsers = {}; @@ -710,6 +715,9 @@ ExportMap.parse = function (path, content, context) { m.namespace.set('default', {}); // add default export } + if (unambiguouslyESM) { + m.parseGoal = 'Module'; + } return m; }; diff --git a/src/rules/named.js b/src/rules/named.js index 24e6bc0ac5..a529c295b7 100644 --- a/src/rules/named.js +++ b/src/rules/named.js @@ -40,7 +40,7 @@ module.exports = { } const imports = Exports.get(node.source.value, context); - if (imports == null) { + if (imports == null || imports.parseGoal === 'ambiguous') { return; } @@ -97,6 +97,7 @@ module.exports = { // return if it's not a string source || source.type !== 'Literal' || variableExports == null + || variableExports.parseGoal === 'ambiguous' ) { return; } diff --git a/tests/files/dynamic-import-in-commonjs.js b/tests/files/dynamic-import-in-commonjs.js new file mode 100644 index 0000000000..259feb4cd9 --- /dev/null +++ b/tests/files/dynamic-import-in-commonjs.js @@ -0,0 +1,5 @@ +async function doSomething() { + await import('./bar.js'); +} + +exports.something = 'hello'; diff --git a/tests/src/rules/named.js b/tests/src/rules/named.js index 992baa0fd0..10ba76b79d 100644 --- a/tests/src/rules/named.js +++ b/tests/src/rules/named.js @@ -190,7 +190,16 @@ ruleTester.run('named', rule, { sourceType: 'module', ecmaVersion: 2020, }, - })) || []), + })), + + testVersion('>=7.8.0', () =>({ code: 'const { something } = require("./dynamic-import-in-commonjs")', + parserOptions: { ecmaVersion: 2021 }, + options: [{ commonjs: true }], + })), + + testVersion('>=7.8.0', () => ({ code: 'import { something } from "./dynamic-import-in-commonjs"', + parserOptions: { ecmaVersion: 2021 } })), + ), ], invalid: [ From 210e40a11815bf8ae573324c2449933dcc1df5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Fri, 31 Dec 2021 00:27:03 +0100 Subject: [PATCH 02/43] [utils] [patch] Fix `@babel/eslint-parser` 8 compatibility --- utils/CHANGELOG.md | 7 ++++++- utils/parse.js | 21 +++++++-------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/utils/CHANGELOG.md b/utils/CHANGELOG.md index 65d67a35c7..e299e01b01 100644 --- a/utils/CHANGELOG.md +++ b/utils/CHANGELOG.md @@ -5,10 +5,13 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## Unreleased +### Fixed +- [patch] Fix `@babel/eslint-parser` 8 compatibility ([#2343], thanks [@nicolo-ribaudo]) + ## v2.7.1 - 2021-10-13 ### Fixed - - fixed SyntaxError in node <= 6: Unexpected token ) in parse.js ([#2261], thanks [@VitusFW]) +- fixed SyntaxError in node <= 6: Unexpected token ) in parse.js ([#2261], thanks [@VitusFW]) ## v2.7.0 - 2021-10-11 @@ -102,6 +105,7 @@ Yanked due to critical issue with cache key resulting from #839. ### Fixed - `unambiguous.test()` regex is now properly in multiline mode +[#2343]: https://github.com/import-js/eslint-plugin-import/pull/2343 [#2261]: https://github.com/import-js/eslint-plugin-import/pull/2261 [#2212]: https://github.com/import-js/eslint-plugin-import/pull/2212 [#2160]: https://github.com/import-js/eslint-plugin-import/pull/2160 @@ -138,6 +142,7 @@ Yanked due to critical issue with cache key resulting from #839. [@manuth]: https://github.com/manuth [@maxkomarychev]: https://github.com/maxkomarychev [@mgwalker]: https://github.com/mgwalker +[@nicolo-ribaudo]: https://github.com/nicolo-ribaudo [@pmcelhaney]: https://github.com/pmcelhaney [@sergei-startsev]: https://github.com/sergei-startsev [@sompylasar]: https://github.com/sompylasar diff --git a/utils/parse.js b/utils/parse.js index a771544ea7..810533ed52 100644 --- a/utils/parse.js +++ b/utils/parse.js @@ -7,34 +7,27 @@ const fs = require('fs'); const log = require('debug')('eslint-plugin-import:parse'); -function getBabelVisitorKeys(parserPath) { +function getBabelEslintVisitorKeys(parserPath) { if (parserPath.endsWith('index.js')) { const hypotheticalLocation = parserPath.replace('index.js', 'visitor-keys.js'); if (fs.existsSync(hypotheticalLocation)) { const keys = moduleRequire(hypotheticalLocation); return keys.default || keys; } - } else if (parserPath.endsWith('index.cjs')) { - const hypotheticalLocation = parserPath.replace('index.cjs', 'worker/ast-info.cjs'); - if (fs.existsSync(hypotheticalLocation)) { - const astInfo = moduleRequire(hypotheticalLocation); - return astInfo.getVisitorKeys(); - } } return null; } function keysFromParser(parserPath, parserInstance, parsedResult) { + // Exposed by @typescript-eslint/parser and @babel/eslint-parser + if (parsedResult && parsedResult.visitorKeys) { + return parsedResult.visitorKeys; + } if (/.*espree.*/.test(parserPath)) { return parserInstance.VisitorKeys; } - if (/.*(babel-eslint|@babel\/eslint-parser).*/.test(parserPath)) { - return getBabelVisitorKeys(parserPath); - } - if (/.*@typescript-eslint\/parser/.test(parserPath)) { - if (parsedResult) { - return parsedResult.visitorKeys; - } + if (/.*babel-eslint.*/.test(parserPath)) { + return getBabelEslintVisitorKeys(parserPath); } return null; } From 9e06effc700b1c1bd5ada3bff42fefa668d99b6d Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 1 Jan 2022 22:33:29 -0800 Subject: [PATCH 03/43] [utils] [Refactor] inline `pkgDir` implementation; remove `pkg-dir` --- utils/CHANGELOG.md | 3 +++ utils/package.json | 3 +-- utils/pkgDir.js | 11 +++++++++++ utils/resolve.js | 5 ++--- 4 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 utils/pkgDir.js diff --git a/utils/CHANGELOG.md b/utils/CHANGELOG.md index e299e01b01..0ac0704b1d 100644 --- a/utils/CHANGELOG.md +++ b/utils/CHANGELOG.md @@ -8,6 +8,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Fixed - [patch] Fix `@babel/eslint-parser` 8 compatibility ([#2343], thanks [@nicolo-ribaudo]) +### Changed +- [Refactor] inline `pkgDir` implementation; remove `pkg-dir` + ## v2.7.1 - 2021-10-13 ### Fixed diff --git a/utils/package.json b/utils/package.json index 2c4c53bd47..5c68ed6263 100644 --- a/utils/package.json +++ b/utils/package.json @@ -27,7 +27,6 @@ "homepage": "https://github.com/import-js/eslint-plugin-import#readme", "dependencies": { "debug": "^3.2.7", - "find-up": "^2.1.0", - "pkg-dir": "^2.0.0" + "find-up": "^2.1.0" } } diff --git a/utils/pkgDir.js b/utils/pkgDir.js new file mode 100644 index 0000000000..34412202f1 --- /dev/null +++ b/utils/pkgDir.js @@ -0,0 +1,11 @@ +'use strict'; + +const path = require('path'); +const pkgUp = require('./pkgUp').default; + +exports.__esModule = true; + +exports.default = function (cwd) { + const fp = pkgUp({ cwd }); + return fp ? path.dirname(fp) : null; +}; diff --git a/utils/resolve.js b/utils/resolve.js index 31fb659079..4a35c6a472 100644 --- a/utils/resolve.js +++ b/utils/resolve.js @@ -1,14 +1,13 @@ 'use strict'; exports.__esModule = true; -const pkgDir = require('pkg-dir'); - const fs = require('fs'); const Module = require('module'); const path = require('path'); const hashObject = require('./hash').hashObject; const ModuleCache = require('./ModuleCache').default; +const pkgDir = require('./pkgDir').default; const CASE_SENSITIVE_FS = !fs.existsSync(path.join(__dirname.toUpperCase(), 'reSOLVE.js')); exports.CASE_SENSITIVE_FS = CASE_SENSITIVE_FS; @@ -175,7 +174,7 @@ function resolverReducer(resolvers, map) { } function getBaseDir(sourceFile) { - return pkgDir.sync(sourceFile) || process.cwd(); + return pkgDir(sourceFile) || process.cwd(); } function requireResolver(name, sourceFile) { // Try to resolve package with conventional name From de8873973a3185b8d4a5c4bbbf6f2f22c4abc288 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 1 Jan 2022 22:36:27 -0800 Subject: [PATCH 04/43] utils: v2.7.2 --- utils/CHANGELOG.md | 2 ++ utils/package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/utils/CHANGELOG.md b/utils/CHANGELOG.md index 0ac0704b1d..487a064464 100644 --- a/utils/CHANGELOG.md +++ b/utils/CHANGELOG.md @@ -5,6 +5,8 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## Unreleased +## v2.7.2 - 2022-01-01 + ### Fixed - [patch] Fix `@babel/eslint-parser` 8 compatibility ([#2343], thanks [@nicolo-ribaudo]) diff --git a/utils/package.json b/utils/package.json index 5c68ed6263..97f95162f1 100644 --- a/utils/package.json +++ b/utils/package.json @@ -1,6 +1,6 @@ { "name": "eslint-module-utils", - "version": "2.7.1", + "version": "2.7.2", "description": "Core utilities to support eslint-plugin-import and other module-related plugins.", "engines": { "node": ">=4" From ef9368150b2ecab02acd6642181ec3e0ba852261 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 1 Jan 2022 22:54:43 -0800 Subject: [PATCH 05/43] [Deps] update `eslint-module-utils` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a48f86b225..318a9e4346 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.1", + "eslint-module-utils": "^2.7.2", "has": "^1.0.3", "is-core-module": "^2.8.0", "is-glob": "^4.0.3", From dbf668e99026c211ced6125297f47f38e0e866c7 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 1 Jan 2022 22:55:45 -0800 Subject: [PATCH 06/43] [Dev Deps] update `safe-publish-latest` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 318a9e4346..170820de4a 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "nyc": "^11.9.0", "redux": "^3.7.2", "rimraf": "^2.7.1", - "safe-publish-latest": "^1.1.4", + "safe-publish-latest": "^2.0.0", "semver": "^6.3.0", "sinon": "^2.4.1", "typescript": "^2.8.1 || ~3.9.5", From 68cea3e6b6fe5fd61e5cf2e2c7c0be9e8dc597cb Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 2 Jan 2022 13:30:53 -0800 Subject: [PATCH 07/43] Bump to v2.25.4 --- CHANGELOG.md | 6 +++++- package.json | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf11927c7c..3894d312c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] +## [2.25.4] - 2022-01-02 + ### Fixed - `importType`: avoid crashing on a non-string' ([#2305], thanks [@ljharb]) - [`first`]: prevent crash when parsing angular templates ([#2210], thanks [@ljharb]) @@ -15,6 +17,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Changed - [`no-default-import`]: report on the token "default" instead of the entire node ([#2299], thanks [@pmcelhaney]) - [Docs] [`order`]: Remove duplicate mention of default ([#2280], thanks [@johnthagen]) +- [Deps] update `eslint-module-utils` ## [2.25.3] - 2021-11-09 @@ -1344,7 +1347,8 @@ for info on changes for earlier releases. [#119]: https://github.com/import-js/eslint-plugin-import/issues/119 [#89]: https://github.com/import-js/eslint-plugin-import/issues/89 -[Unreleased]: https://github.com/import-js/eslint-plugin-import/compare/v2.25.3...HEAD +[Unreleased]: https://github.com/import-js/eslint-plugin-import/compare/v2.25.4...HEAD +[2.25.4]: https://github.com/import-js/eslint-plugin-import/compare/v2.25.3...v2.25.4 [2.25.3]: https://github.com/import-js/eslint-plugin-import/compare/v2.25.2...v2.25.3 [2.25.2]: https://github.com/import-js/eslint-plugin-import/compare/v2.25.1...v2.25.2 [2.25.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.25.0...v2.25.1 diff --git a/package.json b/package.json index 170820de4a..07068866be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-import", - "version": "2.25.3", + "version": "2.25.4", "description": "Import with sanity.", "engines": { "node": ">=4" From df8c1a8c3b7365ca057305996012dc536f44c512 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 11 Jan 2022 22:28:04 -0800 Subject: [PATCH 08/43] [utils] [Fix] `parse`: restore compatibility by making the return value `ast` again Fixes #2350 --- utils/CHANGELOG.md | 4 ++++ utils/parse.js | 27 ++++++++++++++------------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/utils/CHANGELOG.md b/utils/CHANGELOG.md index 487a064464..7e36bb9fd6 100644 --- a/utils/CHANGELOG.md +++ b/utils/CHANGELOG.md @@ -5,6 +5,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## Unreleased +### Fixed +- [Fix] `parse`: restore compatibility by making the return value `ast` again ([#2350], thanks [@ljharb]) + ## v2.7.2 - 2022-01-01 ### Fixed @@ -110,6 +113,7 @@ Yanked due to critical issue with cache key resulting from #839. ### Fixed - `unambiguous.test()` regex is now properly in multiline mode +[#2350]: https://github.com/import-js/eslint-plugin-import/issues/2350 [#2343]: https://github.com/import-js/eslint-plugin-import/pull/2343 [#2261]: https://github.com/import-js/eslint-plugin-import/pull/2261 [#2212]: https://github.com/import-js/eslint-plugin-import/pull/2212 diff --git a/utils/parse.js b/utils/parse.js index 810533ed52..98e8215992 100644 --- a/utils/parse.js +++ b/utils/parse.js @@ -32,6 +32,16 @@ function keysFromParser(parserPath, parserInstance, parsedResult) { return null; } +// this exists to smooth over the unintentional breaking change in v2.7. +// TODO, semver-major: avoid mutating `ast` and return a plain object instead. +function makeParseReturn(ast, visitorKeys) { + if (ast) { + ast.visitorKeys = visitorKeys; + ast.ast = ast; + } + return ast; +} + exports.default = function parse(path, content, context) { if (context == null) throw new Error('need context to parse properly'); @@ -73,10 +83,7 @@ exports.default = function parse(path, content, context) { try { const parserRaw = parser.parseForESLint(content, parserOptions); ast = parserRaw.ast; - return { - ast, - visitorKeys: keysFromParser(parserPath, parser, parserRaw), - }; + return makeParseReturn(ast, keysFromParser(parserPath, parser, parserRaw)); } catch (e) { console.warn(); console.warn('Error while parsing ' + parserOptions.filePath); @@ -89,18 +96,12 @@ exports.default = function parse(path, content, context) { '` is invalid and will just be ignored' ); } else { - return { - ast, - visitorKeys: keysFromParser(parserPath, parser, undefined), - }; + return makeParseReturn(ast, keysFromParser(parserPath, parser, undefined)); } } - const keys = keysFromParser(parserPath, parser, undefined); - return { - ast: parser.parse(content, parserOptions), - visitorKeys: keys, - }; + const ast = parser.parse(content, parserOptions); + return makeParseReturn(ast, keysFromParser(parserPath, parser, undefined)); }; function getParserPath(path, context) { From 9e4c9a9a06020ed272e49a278c391fcf267e66af Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 11 Jan 2022 22:47:21 -0800 Subject: [PATCH 09/43] [Deps] update `is-core-module`, `resolve` --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 07068866be..6f618421d0 100644 --- a/package.json +++ b/package.json @@ -106,11 +106,11 @@ "eslint-import-resolver-node": "^0.3.6", "eslint-module-utils": "^2.7.2", "has": "^1.0.3", - "is-core-module": "^2.8.0", + "is-core-module": "^2.8.1", "is-glob": "^4.0.3", "minimatch": "^3.0.4", "object.values": "^1.1.5", - "resolve": "^1.20.0", + "resolve": "^1.21.0", "tsconfig-paths": "^3.12.0" } } From 1cb0dc735f8a6eab77d1b9f29cde2439cc0c5e31 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sat, 22 Jan 2022 11:20:01 +0900 Subject: [PATCH 10/43] [Tests] `no-nodejs-modules`: add tests for node protocol URL --- CHANGELOG.md | 7 ++- tests/src/rules/no-nodejs-modules.js | 70 ++++++++++++++++++++++++++-- 2 files changed, 72 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3894d312c1..bd878a8b11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] +### Changed +- [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) + ## [2.25.4] - 2022-01-02 ### Fixed @@ -955,6 +958,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2367]: https://github.com/import-js/eslint-plugin-import/pull/2367 [#2341]: https://github.com/import-js/eslint-plugin-import/pull/2341 [#2334]: https://github.com/import-js/eslint-plugin-import/pull/2334 [#2305]: https://github.com/import-js/eslint-plugin-import/pull/2305 @@ -1618,6 +1622,7 @@ for info on changes for earlier releases. [@skyrpex]: https://github.com/skyrpex [@sompylasar]: https://github.com/sompylasar [@soryy708]: https://github.com/soryy708 +[@sosukesuzuki]: https://github.com/sosukesuzuki [@spalger]: https://github.com/spalger [@st-sloth]: https://github.com/st-sloth [@stekycz]: https://github.com/stekycz @@ -1646,4 +1651,4 @@ for info on changes for earlier releases. [@wtgtybhertgeghgtwtg]: https://github.com/wtgtybhertgeghgtwtg [@xpl]: https://github.com/xpl [@yordis]: https://github.com/yordis -[@zloirock]: https://github.com/zloirock +[@zloirock]: https://github.com/zloirock \ No newline at end of file diff --git a/tests/src/rules/no-nodejs-modules.js b/tests/src/rules/no-nodejs-modules.js index 3587a71dca..9be605709a 100644 --- a/tests/src/rules/no-nodejs-modules.js +++ b/tests/src/rules/no-nodejs-modules.js @@ -1,6 +1,7 @@ import { test } from '../utils'; import { RuleTester } from 'eslint'; +const isCore = require('is-core-module'); const ruleTester = new RuleTester(); const rule = require('rules/no-nodejs-modules'); @@ -10,7 +11,7 @@ const error = message => ({ }); ruleTester.run('no-nodejs-modules', rule, { - valid: [ + valid: [].concat( test({ code: 'import _ from "lodash"' }), test({ code: 'import find from "lodash.find"' }), test({ code: 'import foo from "./foo"' }), @@ -55,8 +56,42 @@ ruleTester.run('no-nodejs-modules', rule, { allow: ['path', 'events'], }], }), - ], - invalid: [ + isCore('node:events') ? [ + test({ + code: 'import events from "node:events"', + options: [{ + allow: ['node:events'], + }], + }), + test({ + code: 'var events = require("node:events")', + options: [{ + allow: ['node:events'], + }], + }), + ]: [], + isCore('node:path') ? [ + test({ + code: 'import path from "node:path"', + options: [{ + allow: ['node:path'], + }], + }), + test({ + code: 'var path = require("node:path")', + options: [{ + allow: ['node:path'], + }], + }), + ] : [], + isCore('node:path') && isCore('node:events') ? test({ + code: 'import path from "node:path";import events from "node:events"', + options: [{ + allow: ['node:path', 'node:events'], + }], + }) : [], + ), + invalid: [].concat( test({ code: 'import path from "path"', errors: [error('Do not import Node.js builtin module "path"')], @@ -80,5 +115,32 @@ ruleTester.run('no-nodejs-modules', rule, { }], errors: [error('Do not import Node.js builtin module "fs"')], }), - ], + isCore('node:path') ? [ + test({ + code: 'import path from "node:path"', + errors: [error('Do not import Node.js builtin module "node:path"')], + }), + test({ + code: 'var path = require("node:path")', + errors: [error('Do not import Node.js builtin module "node:path"')], + }), + ] : [], + isCore('node:fs') ? [ + test({ + code: 'import fs from "node:fs"', + errors: [error('Do not import Node.js builtin module "node:fs"')], + }), + test({ + code: 'var fs = require("node:fs")', + errors: [error('Do not import Node.js builtin module "node:fs"')], + }), + test({ + code: 'import fs from "node:fs"', + options: [{ + allow: ['node:path'], + }], + errors: [error('Do not import Node.js builtin module "node:fs"')], + }), + ] : [], + ), }); From f795e8f79be7bbb522bab218d1d802f753c4dc4e Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Mon, 17 Jan 2022 17:45:07 +0900 Subject: [PATCH 11/43] [Tests] configure ESLint overrides to parse arbitrary module namespace names --- tests/files/{.eslintrc => .eslintrc.js} | 40 +++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) rename tests/files/{.eslintrc => .eslintrc.js} (85%) diff --git a/tests/files/.eslintrc b/tests/files/.eslintrc.js similarity index 85% rename from tests/files/.eslintrc rename to tests/files/.eslintrc.js index 6d36c133b3..6f25298010 100644 --- a/tests/files/.eslintrc +++ b/tests/files/.eslintrc.js @@ -1,4 +1,9 @@ -{ +const eslintPkg = require('eslint/package.json'); +const semver = require('semver'); + +const supportsArbitraryModuleNamespaceIdentifierNames = semver.satisfies(eslintPkg.version, '>= 8.7'); + +const config = { "parser": "babel-eslint", "parserOptions": { "sourceType": "module", @@ -281,5 +286,36 @@ "import/no-duplicates": 0, "import/no-extraneous-dependencies": 0, "import/unambiguous": 0 - } + }, + ignorePatterns: [ + "default-export-namespace-string.js", + "default-export-string.js", + "export-default-string-and-named.js", + "no-unused-modules/arbitrary-module-namespace-identifier-name-a.js", + "no-unused-modules/arbitrary-module-namespace-identifier-name-b.js", + "no-unused-modules/arbitrary-module-namespace-identifier-name-c.js" + ], +} + +if (supportsArbitraryModuleNamespaceIdentifierNames) { + config.ignorePatterns = []; + config.overrides = [ + // For parsing arbitrary module namespace names + { + "files": [ + "default-export-namespace-string.js", + "default-export-string.js", + "export-default-string-and-named.js", + "no-unused-modules/arbitrary-module-namespace-identifier-name-a.js", + "no-unused-modules/arbitrary-module-namespace-identifier-name-b.js", + "no-unused-modules/arbitrary-module-namespace-identifier-name-c.js" + ], + "parser": "espree", + "parserOptions": { + "ecmaVersion": 2022 + } + } + ]; } + +module.exports = config; From 887e67061de42ac2b416282cd7f92ceb3fb612ee Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sun, 16 Jan 2022 13:39:24 +0900 Subject: [PATCH 12/43] [Tests] `default`, `no-anonymous-default-export`, `no-mutable-exports`, `no-named-as-default-member`, `no-named-as-default`: add tests for arbitrary module namespace names --- CHANGELOG.md | 2 ++ .../files/export-default-string-and-named.js | 4 +++ tests/src/rules/default.js | 14 ++++++-- .../src/rules/no-anonymous-default-export.js | 11 ++++-- tests/src/rules/no-mutable-exports.js | 20 ++++++++--- tests/src/rules/no-named-as-default-member.js | 28 ++++++++++++--- tests/src/rules/no-named-as-default.js | 34 ++++++++++++++++--- 7 files changed, 92 insertions(+), 21 deletions(-) create mode 100644 tests/files/export-default-string-and-named.js diff --git a/CHANGELOG.md b/CHANGELOG.md index bd878a8b11..98703defce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) +- [Tests] `default`, `no-anonymous-default-export`, `no-mutable-exports`, `no-named-as-default-member`, `no-named-as-default`: add tests for arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) ## [2.25.4] - 2022-01-02 @@ -959,6 +960,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md [#2367]: https://github.com/import-js/eslint-plugin-import/pull/2367 +[#2358]: https://github.com/import-js/eslint-plugin-import/pull/2358 [#2341]: https://github.com/import-js/eslint-plugin-import/pull/2341 [#2334]: https://github.com/import-js/eslint-plugin-import/pull/2334 [#2305]: https://github.com/import-js/eslint-plugin-import/pull/2305 diff --git a/tests/files/export-default-string-and-named.js b/tests/files/export-default-string-and-named.js new file mode 100644 index 0000000000..62c7d13c4f --- /dev/null +++ b/tests/files/export-default-string-and-named.js @@ -0,0 +1,4 @@ +const bar = "bar"; +export function foo() {} + +export { bar as "default" } diff --git a/tests/src/rules/default.js b/tests/src/rules/default.js index 0274e43745..c173f83542 100644 --- a/tests/src/rules/default.js +++ b/tests/src/rules/default.js @@ -1,5 +1,5 @@ import path from 'path'; -import { test, SYNTAX_CASES, getTSParsers } from '../utils'; +import { test, testVersion, SYNTAX_CASES, getTSParsers } from '../utils'; import { RuleTester } from 'eslint'; import { CASE_SENSITIVE_FS } from 'eslint-module-utils/resolve'; @@ -8,7 +8,7 @@ const ruleTester = new RuleTester(); const rule = require('rules/default'); ruleTester.run('default', rule, { - valid: [ + valid: [].concat( test({ code: 'import "./malformed.js"' }), test({ code: 'import foo from "./empty-folder";' }), @@ -92,8 +92,16 @@ ruleTester.run('default', rule, { parser: require.resolve('babel-eslint'), }), + // es2022: Arbitrary module namespace identifier names + testVersion('>= 8.7', () => ({ + code: 'export { "default" as bar } from "./bar"', + parserOptions: { + ecmaVersion: 2022, + }, + })), + ...SYNTAX_CASES, - ], + ), invalid: [ test({ diff --git a/tests/src/rules/no-anonymous-default-export.js b/tests/src/rules/no-anonymous-default-export.js index 231f1b667d..0428ee1b99 100644 --- a/tests/src/rules/no-anonymous-default-export.js +++ b/tests/src/rules/no-anonymous-default-export.js @@ -1,4 +1,4 @@ -import { test, SYNTAX_CASES } from '../utils'; +import { test, testVersion, SYNTAX_CASES } from '../utils'; import { RuleTester } from 'eslint'; @@ -6,7 +6,7 @@ const ruleTester = new RuleTester(); const rule = require('rules/no-anonymous-default-export'); ruleTester.run('no-anonymous-default-export', rule, { - valid: [ + valid: [].concat( // Exports with identifiers are valid test({ code: 'const foo = 123\nexport default foo' }), test({ code: 'export default function foo() {}' }), @@ -31,12 +31,17 @@ ruleTester.run('no-anonymous-default-export', rule, { test({ code: 'export * from \'foo\'' }), test({ code: 'const foo = 123\nexport { foo }' }), test({ code: 'const foo = 123\nexport { foo as default }' }), + // es2022: Arbitrary module namespace identifier names + testVersion('>= 8.7', () => ({ + code: 'const foo = 123\nexport { foo as "default" }', + parserOptions: { ecmaVersion: 2022 }, + })), // Allow call expressions by default for backwards compatibility test({ code: 'export default foo(bar)' }), ...SYNTAX_CASES, - ], + ), invalid: [ test({ code: 'export default []', errors: [{ message: 'Assign array to a variable before exporting as module default' }] }), diff --git a/tests/src/rules/no-mutable-exports.js b/tests/src/rules/no-mutable-exports.js index 2ecae48cdb..e96b841b5c 100644 --- a/tests/src/rules/no-mutable-exports.js +++ b/tests/src/rules/no-mutable-exports.js @@ -1,11 +1,11 @@ -import { test } from '../utils'; +import { test, testVersion } from '../utils'; import { RuleTester } from 'eslint'; import rule from 'rules/no-mutable-exports'; const ruleTester = new RuleTester(); ruleTester.run('no-mutable-exports', rule, { - valid: [ + valid: [].concat( test({ code: 'export const count = 1' }), test({ code: 'export function getCount() {}' }), test({ code: 'export class Counter {}' }), @@ -32,8 +32,12 @@ ruleTester.run('no-mutable-exports', rule, { parser: require.resolve('babel-eslint'), code: 'type Foo = {}\nexport type {Foo}', }), - ], - invalid: [ + // es2022: Arbitrary module namespace identifier names + testVersion('>= 8.7', () => ({ + code: 'const count = 1\nexport { count as "counter" }', parserOptions: { ecmaVersion: 2022 }, + })), + ), + invalid: [].concat( test({ code: 'export let count = 1', errors: ['Exporting mutable \'let\' binding, use \'const\' instead.'], @@ -66,6 +70,12 @@ ruleTester.run('no-mutable-exports', rule, { code: 'var count = 1\nexport default count', errors: ['Exporting mutable \'var\' binding, use \'const\' instead.'], }), + // es2022: Arbitrary module namespace identifier names + testVersion('>= 8.7', () => ({ + code: 'let count = 1\nexport { count as "counter" }', + errors: ['Exporting mutable \'let\' binding, use \'const\' instead.'], + parserOptions: { ecmaVersion: 2022 }, + })), // todo: undeclared globals // test({ @@ -76,5 +86,5 @@ ruleTester.run('no-mutable-exports', rule, { // code: 'count = 1\nexport default count', // errors: ['Exporting mutable global binding, use \'const\' instead.'], // }), - ], + ), }); diff --git a/tests/src/rules/no-named-as-default-member.js b/tests/src/rules/no-named-as-default-member.js index b4f3cf5896..53cba230ba 100644 --- a/tests/src/rules/no-named-as-default-member.js +++ b/tests/src/rules/no-named-as-default-member.js @@ -1,21 +1,27 @@ -import { test, SYNTAX_CASES } from '../utils'; +import { test, testVersion, SYNTAX_CASES } from '../utils'; import { RuleTester } from 'eslint'; import rule from 'rules/no-named-as-default-member'; const ruleTester = new RuleTester(); ruleTester.run('no-named-as-default-member', rule, { - valid: [ + valid: [].concat( test({ code: 'import bar, {foo} from "./bar";' }), test({ code: 'import bar from "./bar"; const baz = bar.baz' }), test({ code: 'import {foo} from "./bar"; const baz = foo.baz;' }), test({ code: 'import * as named from "./named-exports"; const a = named.a' }), test({ code: 'import foo from "./default-export-default-property"; const a = foo.default' }), + // es2022: Arbitrary module namespace identifier names + testVersion('>= 8.7', () => ({ + code: 'import bar, { foo } from "./export-default-string-and-named"', + parserOptions: { ecmaVersion: 2022 }, + })), + ...SYNTAX_CASES, - ], + ), - invalid: [ + invalid: [].concat( test({ code: 'import bar from "./bar"; const foo = bar.foo;', errors: [{ @@ -56,5 +62,17 @@ ruleTester.run('no-named-as-default-member', rule, { type: 'Identifier', }], }), - ], + // es2022: Arbitrary module namespace identifier names + testVersion('>= 8.7', () => ({ + code: 'import bar from "./export-default-string-and-named"; const foo = bar.foo;', + errors: [{ + message: ( + 'Caution: `bar` also has a named export `foo`. ' + + 'Check if you meant to write `import {foo} from \'./export-default-string-and-named\'` instead.' + ), + type: 'MemberExpression', + }], + parserOptions: { ecmaVersion: 2022 }, + })), + ), }); diff --git a/tests/src/rules/no-named-as-default.js b/tests/src/rules/no-named-as-default.js index 57b2f53bd8..129419350e 100644 --- a/tests/src/rules/no-named-as-default.js +++ b/tests/src/rules/no-named-as-default.js @@ -1,11 +1,11 @@ -import { test, SYNTAX_CASES } from '../utils'; +import { test, testVersion, SYNTAX_CASES } from '../utils'; import { RuleTester } from 'eslint'; const ruleTester = new RuleTester(); const rule = require('rules/no-named-as-default'); ruleTester.run('no-named-as-default', rule, { - valid: [ + valid: [].concat( test({ code: 'import "./malformed.js"' }), test({ code: 'import bar, { foo } from "./bar";' }), @@ -21,10 +21,16 @@ ruleTester.run('no-named-as-default', rule, { test({ code: 'export default from "./bar";', parser: require.resolve('babel-eslint') }), + // es2022: Arbitrary module namespae identifier names + testVersion('>= 8.7', () => ({ + code: 'import bar, { foo } from "./export-default-string-and-named"', + parserOptions: { ecmaVersion: 2022 }, + })), + ...SYNTAX_CASES, - ], + ), - invalid: [ + invalid: [].concat( test({ code: 'import foo from "./bar";', errors: [ { @@ -57,5 +63,23 @@ ruleTester.run('no-named-as-default', rule, { type: 'Literal', }], }), - ], + + // es2022: Arbitrary module namespae identifier names + testVersion('>= 8.7', () => ({ + code: 'import foo from "./export-default-string-and-named"', + errors: [{ + message: 'Using exported name \'foo\' as identifier for default export.', + type: 'ImportDefaultSpecifier', + }], + parserOptions: { ecmaVersion: 2022 }, + })), + testVersion('>= 8.7', () => ({ + code: 'import foo, { foo as bar } from "./export-default-string-and-named"', + errors: [{ + message: 'Using exported name \'foo\' as identifier for default export.', + type: 'ImportDefaultSpecifier', + }], + parserOptions: { ecmaVersion: 2022 }, + })), + ), }); From e9a33279a4cc0b9e8f23349100349f6d85eb9f0a Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sat, 15 Jan 2022 14:38:07 +0900 Subject: [PATCH 13/43] [New] `no-named-default`: support arbitrary module namespace names --- CHANGELOG.md | 3 +++ src/rules/no-named-default.js | 2 +- tests/src/rules/no-named-default.js | 18 +++++++++++++++--- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98703defce..2c87b3aac2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] +### Added +- [`no-named-default`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) + ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) - [Tests] `default`, `no-anonymous-default-export`, `no-mutable-exports`, `no-named-as-default-member`, `no-named-as-default`: add tests for arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) diff --git a/src/rules/no-named-default.js b/src/rules/no-named-default.js index 116c89cf57..6a5c1db703 100644 --- a/src/rules/no-named-default.js +++ b/src/rules/no-named-default.js @@ -17,7 +17,7 @@ module.exports = { return; } - if (im.type === 'ImportSpecifier' && im.imported.name === 'default') { + if (im.type === 'ImportSpecifier' && (im.imported.name || im.imported.value) === 'default') { context.report({ node: im.local, message: `Use default import syntax to import '${im.local.name}'.` }); diff --git a/tests/src/rules/no-named-default.js b/tests/src/rules/no-named-default.js index 56470f2bac..1ed1502871 100644 --- a/tests/src/rules/no-named-default.js +++ b/tests/src/rules/no-named-default.js @@ -1,4 +1,4 @@ -import { test, SYNTAX_CASES } from '../utils'; +import { test, testVersion, SYNTAX_CASES } from '../utils'; import { RuleTester } from 'eslint'; const ruleTester = new RuleTester(); @@ -22,7 +22,7 @@ ruleTester.run('no-named-default', rule, { ...SYNTAX_CASES, ], - invalid: [ + invalid: [].concat( /*test({ code: 'import { default } from "./bar";', errors: [{ @@ -45,5 +45,17 @@ ruleTester.run('no-named-default', rule, { type: 'Identifier', }], }), - ], + + // es2022: Arbitrary module namespae identifier names + testVersion('>= 8.7', () => ({ + code: 'import { "default" as bar } from "./bar";', + errors: [{ + message: 'Use default import syntax to import \'bar\'.', + type: 'Identifier', + }], + parserOptions: { + ecmaVersion: 2022, + }, + })) || [], + ), }); From c296b0bedb58e2f905d306bac2f1b4d5ef8adecc Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sat, 15 Jan 2022 15:05:52 +0900 Subject: [PATCH 14/43] [New] `no-default-export`: support arbitrary module namespace names --- CHANGELOG.md | 2 +- src/rules/no-default-export.js | 2 +- tests/src/rules/no-default-export.js | 11 +++++++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c87b3aac2..755ebec2a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] ### Added -- [`no-named-default`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) +- [`no-named-default`, `no-default-export`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) diff --git a/src/rules/no-default-export.js b/src/rules/no-default-export.js index a17428c563..ed1aaf8db6 100644 --- a/src/rules/no-default-export.js +++ b/src/rules/no-default-export.js @@ -25,7 +25,7 @@ module.exports = { }, ExportNamedDeclaration(node) { - node.specifiers.filter(specifier => specifier.exported.name === 'default').forEach(specifier => { + node.specifiers.filter(specifier => (specifier.exported.name || specifier.exported.value) === 'default').forEach(specifier => { const { loc } = context.getSourceCode().getFirstTokens(node)[1] || {}; if (specifier.type === 'ExportDefaultSpecifier') { context.report({ node, message: preferNamed, loc }); diff --git a/tests/src/rules/no-default-export.js b/tests/src/rules/no-default-export.js index 61e55d9593..1d149421f9 100644 --- a/tests/src/rules/no-default-export.js +++ b/tests/src/rules/no-default-export.js @@ -162,5 +162,16 @@ ruleTester.run('no-default-export', rule, { }, ], }), + // es2022: Arbitrary module namespae identifier names + testVersion('>= 8.7', () => ({ + code: 'let foo; export { foo as "default" }', + errors: [ + { + type: 'ExportNamedDeclaration', + message: 'Do not alias `foo` as `default`. Just export `foo` itself instead.', + }, + ], + parserOptions: { ecmaVersion: 2022 }, + })), ), }); From 16c5add20b4aa526426e427efab57f67857dbb52 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sat, 15 Jan 2022 15:11:46 +0900 Subject: [PATCH 15/43] [New] `prefer-default-export`: support arbitrary module namespace names --- CHANGELOG.md | 2 +- src/rules/prefer-default-export.js | 2 +- tests/src/rules/prefer-default-export.js | 11 ++++++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 755ebec2a0..b70aa88e73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] ### Added -- [`no-named-default`, `no-default-export`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) +- [`no-named-default`, `no-default-export`, `prefer-default-export`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) diff --git a/src/rules/prefer-default-export.js b/src/rules/prefer-default-export.js index a8c52bb1a0..230efad12f 100644 --- a/src/rules/prefer-default-export.js +++ b/src/rules/prefer-default-export.js @@ -40,7 +40,7 @@ module.exports = { }, 'ExportSpecifier': function (node) { - if (node.exported.name === 'default') { + if ((node.exported.name || node.exported.value) === 'default') { hasDefaultExport = true; } else { specifierExportCount++; diff --git a/tests/src/rules/prefer-default-export.js b/tests/src/rules/prefer-default-export.js index 36205b1935..d00a99668b 100644 --- a/tests/src/rules/prefer-default-export.js +++ b/tests/src/rules/prefer-default-export.js @@ -1,4 +1,4 @@ -import { test, getNonDefaultParsers } from '../utils'; +import { test, testVersion, getNonDefaultParsers } from '../utils'; import { RuleTester } from 'eslint'; @@ -6,7 +6,7 @@ const ruleTester = new RuleTester(); const rule = require('../../../src/rules/prefer-default-export'); ruleTester.run('prefer-default-export', rule, { - valid: [ + valid: [].concat( test({ code: ` export const foo = 'foo'; @@ -94,7 +94,12 @@ ruleTester.run('prefer-default-export', rule, { `, parser: require.resolve('babel-eslint'), }), - ], + // es2022: Arbitrary module namespae identifier names + testVersion('>= 8.7', () => ({ + code: 'let foo; export { foo as "default" };', + parserOptions: { ecmaVersion: 2022 }, + })), + ), invalid: [ test({ code: ` From 8ee2497fd5c9d62c1a167f7b0680295fb07a3204 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sat, 15 Jan 2022 15:20:46 +0900 Subject: [PATCH 16/43] [New] `no-named-export`: support arbitrary module namespace names --- CHANGELOG.md | 2 +- src/rules/no-named-export.js | 2 +- tests/src/rules/no-named-export.js | 12 +++++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b70aa88e73..d2f11444fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] ### Added -- [`no-named-default`, `no-default-export`, `prefer-default-export`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) +- [`no-named-default`, `no-default-export`, `prefer-default-export`, `no-named-export`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) diff --git a/src/rules/no-named-export.js b/src/rules/no-named-export.js index bb586ead00..6c92ad9cae 100644 --- a/src/rules/no-named-export.js +++ b/src/rules/no-named-export.js @@ -25,7 +25,7 @@ module.exports = { return context.report({ node, message }); } - const someNamed = node.specifiers.some(specifier => specifier.exported.name !== 'default'); + const someNamed = node.specifiers.some(specifier => (specifier.exported.name || specifier.exported.value) !== 'default'); if (someNamed) { context.report({ node, message }); } diff --git a/tests/src/rules/no-named-export.js b/tests/src/rules/no-named-export.js index 41d0fcd7cf..4d8978b35f 100644 --- a/tests/src/rules/no-named-export.js +++ b/tests/src/rules/no-named-export.js @@ -1,11 +1,11 @@ import { RuleTester } from 'eslint'; -import { test } from '../utils'; +import { test, testVersion } from '../utils'; const ruleTester = new RuleTester(); const rule = require('rules/no-named-export'); ruleTester.run('no-named-export', rule, { - valid: [ + valid: [].concat( test({ code: 'export default function bar() {};', }), @@ -27,7 +27,13 @@ ruleTester.run('no-named-export', rule, { test({ code: `import {default as foo} from './foo';`, }), - ], + + // es2022: Arbitrary module namespae identifier names + testVersion('>= 8.7', () => ({ + code: 'let foo; export { foo as "default" }', + parserOptions: { ecmaVersion: 2022 }, + })), + ), invalid: [ test({ code: ` From 4382b345e777a58a576029a755d5c72051a07542 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sun, 16 Jan 2022 13:51:12 +0900 Subject: [PATCH 17/43] [New] `export`: support arbitrary namespace names --- CHANGELOG.md | 2 +- src/rules/export.js | 2 +- tests/src/rules/export.js | 16 ++++++++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2f11444fd..a223919d1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] ### Added -- [`no-named-default`, `no-default-export`, `prefer-default-export`, `no-named-export`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) +- [`no-named-default`, `no-default-export`, `prefer-default-export`, `no-named-export`, `export`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) diff --git a/src/rules/export.js b/src/rules/export.js index e0b2c57857..b9378f0910 100644 --- a/src/rules/export.js +++ b/src/rules/export.js @@ -88,7 +88,7 @@ module.exports = { 'ExportDefaultDeclaration': (node) => addNamed('default', node, getParent(node)), 'ExportSpecifier': (node) => addNamed( - node.exported.name, + node.exported.name || node.exported.value, node.exported, getParent(node.parent), ), diff --git a/tests/src/rules/export.js b/tests/src/rules/export.js index 29fae41015..d075aea6bd 100644 --- a/tests/src/rules/export.js +++ b/tests/src/rules/export.js @@ -46,7 +46,7 @@ ruleTester.run('export', rule, { })) || [], ), - invalid: [ + invalid: [].concat( // multiple defaults // test({ // code: 'export default foo; export default bar', @@ -122,7 +122,19 @@ ruleTester.run('export', rule, { code: 'export * from "./default-export"', errors: [`No named exports found in module './default-export'.`], }), - ], + + // es2022: Arbitrary module namespace identifier names + testVersion('>= 8.7', () => ({ + code: 'let foo; export { foo as "foo" }; export * from "./export-all"', + errors: [ + 'Multiple exports of name \'foo\'.', + 'Multiple exports of name \'foo\'.', + ], + parserOptions: { + ecmaVersion: 2022, + }, + })), + ), }); From 8cd3a0ef3f52c3385703ab2cb5d6c978a467ebb1 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sun, 16 Jan 2022 14:44:13 +0900 Subject: [PATCH 18/43] [New] `named`: support arbitrary module namespace names --- CHANGELOG.md | 2 +- src/rules/named.js | 8 +++++--- tests/src/rules/named.js | 32 +++++++++++++++++++++++++++----- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a223919d1e..9dcb2a5b7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] ### Added -- [`no-named-default`, `no-default-export`, `prefer-default-export`, `no-named-export`, `export`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) +- [`no-named-default`, `no-default-export`, `prefer-default-export`, `no-named-export`, `export`, `named`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) diff --git a/src/rules/named.js b/src/rules/named.js index a529c295b7..ad1b5e1728 100644 --- a/src/rules/named.js +++ b/src/rules/named.js @@ -58,7 +58,9 @@ module.exports = { return; } - const deepLookup = imports.hasDeep(im[key].name); + const name = im[key].name || im[key].value; + + const deepLookup = imports.hasDeep(name); if (!deepLookup.found) { if (deepLookup.path.length > 1) { @@ -66,9 +68,9 @@ module.exports = { .map(i => path.relative(path.dirname(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()), i.path)) .join(' -> '); - context.report(im[key], `${im[key].name} not found via ${deepPath}`); + context.report(im[key], `${name} not found via ${deepPath}`); } else { - context.report(im[key], im[key].name + ' not found in \'' + node.source.value + '\''); + context.report(im[key], name + ' not found in \'' + node.source.value + '\''); } } }); diff --git a/tests/src/rules/named.js b/tests/src/rules/named.js index 10ba76b79d..af2f9ce185 100644 --- a/tests/src/rules/named.js +++ b/tests/src/rules/named.js @@ -7,9 +7,8 @@ import { CASE_SENSITIVE_FS } from 'eslint-module-utils/resolve'; const ruleTester = new RuleTester(); const rule = require('rules/named'); -function error(name, module) { - return { message: name + ' not found in \'' + module + '\'', - type: 'Identifier' }; +function error(name, module, type = 'Identifier') { + return { message: name + ' not found in \'' + module + '\'', type }; } ruleTester.run('named', rule, { @@ -199,10 +198,16 @@ ruleTester.run('named', rule, { testVersion('>=7.8.0', () => ({ code: 'import { something } from "./dynamic-import-in-commonjs"', parserOptions: { ecmaVersion: 2021 } })), + + // es2022: Arbitrary module namespace identifier names + testVersion('>= 8.7', () => ({ + code: 'import { "foo" as foo } from "./bar"', parserOptions: { ecmaVersion: 2022 } })), + testVersion('>= 8.7', () => ({ + code: 'import { "foo" as foo } from "./empty-module"', parserOptions: { ecmaVersion: 2022 } })), ), ], - invalid: [ + invalid: [].concat( test({ code: 'import { somethingElse } from "./test-module"', errors: [ error('somethingElse', './test-module') ] }), @@ -323,7 +328,24 @@ ruleTester.run('named', rule, { code: 'import { default as barDefault } from "./re-export"', errors: [`default not found in './re-export'`], }), - ], + + // es2022: Arbitrary module namespace identifier names + testVersion('>= 8.7', () => ({ + code: 'import { "somethingElse" as somethingElse } from "./test-module"', + errors: [ error('somethingElse', './test-module', 'Literal') ], + parserOptions: { ecmaVersion: 2022 }, + })), + testVersion('>= 8.7', () => ({ + code: 'import { "baz" as baz, "bop" as bop } from "./bar"', + errors: [error('baz', './bar', 'Literal'), error('bop', './bar', 'Literal')], + parserOptions: { ecmaVersion: 2022 }, + })), + testVersion('>= 8.7', () => ({ + code: 'import { "default" as barDefault } from "./re-export"', + errors: [`default not found in './re-export'`], + parserOptions: { ecmaVersion: 2022 }, + })), + ), }); // #311: import of mismatched case From 37126ec0b74342ec494905ad9f5cb1fe8eb6f3d8 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sun, 16 Jan 2022 16:02:29 +0900 Subject: [PATCH 19/43] [New] `namespace`: support arbitrary module namespace names --- CHANGELOG.md | 2 +- src/ExportMap.js | 4 +- src/rules/namespace.js | 2 +- .../files/default-export-namespace-string.js | 1 + tests/files/default-export-string.js | 3 ++ tests/src/rules/namespace.js | 42 +++++++++++++++++-- 6 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 tests/files/default-export-namespace-string.js create mode 100644 tests/files/default-export-string.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dcb2a5b7a..7397016776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] ### Added -- [`no-named-default`, `no-default-export`, `prefer-default-export`, `no-named-export`, `export`, `named`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) +- [`no-named-default`, `no-default-export`, `prefer-default-export`, `no-named-export`, `export`, `named`, `namespace`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) diff --git a/src/ExportMap.js b/src/ExportMap.js index 564b5d63d8..f11a8752fc 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -479,11 +479,11 @@ ExportMap.parse = function (path, content, context) { })); return; case 'ExportAllDeclaration': - m.namespace.set(s.exported.name, addNamespace(exportMeta, s.source.value)); + m.namespace.set(s.exported.name || s.exported.value, addNamespace(exportMeta, s.source.value)); return; case 'ExportSpecifier': if (!n.source) { - m.namespace.set(s.exported.name, addNamespace(exportMeta, s.local)); + m.namespace.set(s.exported.name || s.exported.value, addNamespace(exportMeta, s.local)); return; } // else falls through diff --git a/src/rules/namespace.js b/src/rules/namespace.js index 6325b88ba2..8a7099df61 100644 --- a/src/rules/namespace.js +++ b/src/rules/namespace.js @@ -69,7 +69,7 @@ module.exports = { case 'ImportSpecifier': { const meta = imports.get( // default to 'default' for default https://i.imgur.com/nj6qAWy.jpg - specifier.imported ? specifier.imported.name : 'default', + specifier.imported ? (specifier.imported.name || specifier.imported.value) : 'default', ); if (!meta || !meta.namespace) { break; } namespaces.set(specifier.local.name, meta.namespace); diff --git a/tests/files/default-export-namespace-string.js b/tests/files/default-export-namespace-string.js new file mode 100644 index 0000000000..5b4a01ab72 --- /dev/null +++ b/tests/files/default-export-namespace-string.js @@ -0,0 +1 @@ +export * as "default" from "./named-exports"; diff --git a/tests/files/default-export-string.js b/tests/files/default-export-string.js new file mode 100644 index 0000000000..4f68b517e6 --- /dev/null +++ b/tests/files/default-export-string.js @@ -0,0 +1,3 @@ +function foo() { return 'bar' } + +export { foo as "default" } diff --git a/tests/src/rules/namespace.js b/tests/src/rules/namespace.js index 826637b970..15ab133b1f 100644 --- a/tests/src/rules/namespace.js +++ b/tests/src/rules/namespace.js @@ -183,10 +183,35 @@ const valid = [ parserOptions: { ecmaVersion: 2020, }, - })) || []), + })), + // es2022: Arbitrary module namespace identifier names + testVersion('>= 8.7', () => ({ + code: "import * as names from './default-export-string';", + parserOptions: { ecmaVersion: 2022 }, + })), + testVersion('>= 8.7', () => ({ + code: "import * as names from './default-export-string'; console.log(names.default)", + parserOptions: { ecmaVersion: 2022 }, + })), + testVersion('>= 8.7', () => ({ + code: "import * as names from './default-export-namespace-string';", + parserOptions: { ecmaVersion: 2022 }, + })), + testVersion('>= 8.7', () => ({ + code: "import * as names from './default-export-namespace-string'; console.log(names.default)", + parserOptions: { ecmaVersion: 2022 }, + })), + testVersion('>= 8.7', () => ({ + code: `import { "b" as b } from "./deep/a"; console.log(b.c.d.e)`, + parserOptions: { ecmaVersion: 2022 }, + })), + testVersion('>= 8.7', () => ({ + code: `import { "b" as b } from "./deep/a"; var {c:{d:{e}}} = b`, + parserOptions: { ecmaVersion: 2022 }, + }))), ]; -const invalid = [ +const invalid = [].concat( test({ code: "import * as names from './named-exports'; " + ' console.log(names.c);', errors: [error('c', 'names')] }), @@ -275,7 +300,18 @@ const invalid = [ }, }), -] + // es2022: Arbitrary module namespace identifier names + testVersion('>= 8.7', () => ({ + code: `import { "b" as b } from "./deep/a"; console.log(b.e)`, + errors: [ "'e' not found in imported namespace 'b'." ], + parserOptions: { ecmaVersion: 2022 }, + })), + testVersion('>= 8.7', () => ({ + code: `import { "b" as b } from "./deep/a"; console.log(b.c.e)`, + errors: [ "'e' not found in deeply imported namespace 'b.c'." ], + parserOptions: { ecmaVersion: 2022 }, + })), +) /////////////////////// // deep dereferences // From 1becbb1ea5073fe592e117cbf2079e31e116d4c7 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Mon, 17 Jan 2022 16:41:23 +0900 Subject: [PATCH 20/43] [New] `no-unused-modules`: support arbitrary module namespace names --- CHANGELOG.md | 2 +- src/ExportMap.js | 2 +- src/rules/no-unused-modules.js | 10 +++--- ...rary-module-namespace-identifier-name-a.js | 2 ++ ...rary-module-namespace-identifier-name-b.js | 1 + ...rary-module-namespace-identifier-name-c.js | 2 ++ tests/src/rules/no-unused-modules.js | 32 ++++++++++++++++++- 7 files changed, 43 insertions(+), 8 deletions(-) create mode 100644 tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-a.js create mode 100644 tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-b.js create mode 100644 tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-c.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 7397016776..63de41cd73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] ### Added -- [`no-named-default`, `no-default-export`, `prefer-default-export`, `no-named-export`, `export`, `named`, `namespace`]: support arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) +- [`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]) ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) diff --git a/src/ExportMap.js b/src/ExportMap.js index f11a8752fc..7425dce44a 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -593,7 +593,7 @@ ExportMap.parse = function (path, content, context) { importedSpecifiers.add(specifier.type); } if (specifier.type === 'ImportSpecifier') { - importedSpecifiers.add(specifier.imported.name); + importedSpecifiers.add(specifier.imported.name || specifier.imported.value); } // import { type Foo } (Flow) diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index 9891d4097e..efc2169946 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -604,7 +604,7 @@ module.exports = { if (specifiers.length > 0) { specifiers.forEach(specifier => { if (specifier.exported) { - newExportIdentifiers.add(specifier.exported.name); + newExportIdentifiers.add(specifier.exported.name || specifier.exported.value); } }); } @@ -715,8 +715,8 @@ module.exports = { if (astNode.source) { resolvedPath = resolve(astNode.source.raw.replace(/('|")/g, ''), context); astNode.specifiers.forEach(specifier => { - const name = specifier.local.name; - if (specifier.local.name === DEFAULT) { + const name = specifier.local.name || specifier.local.value; + if (name === DEFAULT) { newDefaultImports.add(resolvedPath); } else { newImports.set(name, resolvedPath); @@ -753,7 +753,7 @@ module.exports = { specifier.type === IMPORT_NAMESPACE_SPECIFIER) { return; } - newImports.set(specifier.imported.name, resolvedPath); + newImports.set(specifier.imported.name || specifier.imported.value, resolvedPath); }); } }); @@ -942,7 +942,7 @@ module.exports = { }, 'ExportNamedDeclaration': node => { node.specifiers.forEach(specifier => { - checkUsage(node, specifier.exported.name); + checkUsage(node, specifier.exported.name || specifier.exported.value); }); forEachDeclarationIdentifier(node.declaration, (name) => { checkUsage(node, name); diff --git a/tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-a.js b/tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-a.js new file mode 100644 index 0000000000..7ad810de86 --- /dev/null +++ b/tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-a.js @@ -0,0 +1,2 @@ +const foo = 333 +export { foo as "foo" } diff --git a/tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-b.js b/tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-b.js new file mode 100644 index 0000000000..fa7652725e --- /dev/null +++ b/tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-b.js @@ -0,0 +1 @@ +import { "foo" as foo } from "./arbitrary-module-namespace-identifier-name-a.js" diff --git a/tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-c.js b/tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-c.js new file mode 100644 index 0000000000..7ad810de86 --- /dev/null +++ b/tests/files/no-unused-modules/arbitrary-module-namespace-identifier-name-c.js @@ -0,0 +1,2 @@ +const foo = 333 +export { foo as "foo" } diff --git a/tests/src/rules/no-unused-modules.js b/tests/src/rules/no-unused-modules.js index 6f87058c4a..8610ff4aa0 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -1,4 +1,4 @@ -import { test, testFilePath, getTSParsers } from '../utils'; +import { test, testVersion, testFilePath, getTSParsers } from '../utils'; import jsxConfig from '../../../config/react'; import typescriptConfig from '../../../config/typescript'; @@ -1261,3 +1261,33 @@ describe('support (nested) destructuring assignment', () => { invalid: [], }); }); + +describe('support ES2022 Arbitrary module namespace identifier names', () => { + ruleTester.run('no-unused-module', rule, { + valid: [].concat( + testVersion('>= 8.7', () => ({ + options: unusedExportsOptions, + code: `import { "foo" as foo } from "./arbitrary-module-namespace-identifier-name-a"`, + parserOptions: { ecmaVersion: 2022 }, + filename: testFilePath('./no-unused-modules/arbitrary-module-namespace-identifier-name-b.js'), + })), + testVersion('>= 8.7', () => ({ + options: unusedExportsOptions, + code: 'const foo = 333;\nexport { foo as "foo" }', + parserOptions: { ecmaVersion: 2022 }, + filename: testFilePath('./no-unused-modules/arbitrary-module-namespace-identifier-name-a.js'), + })), + ), + invalid: [].concat( + testVersion('>= 8.7', () => ({ + options: unusedExportsOptions, + code: 'const foo = 333\nexport { foo as "foo" }', + parserOptions: { ecmaVersion: 2022 }, + filename: testFilePath('./no-unused-modules/arbitrary-module-namespace-identifier-name-c.js'), + errors: [ + error(`exported declaration 'foo' not used within other modules`), + ], + })), + ), + }); +}); From 7cead4674e363eb055599b5ca229ec75a424c81b Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 23 Jan 2022 08:32:50 -0800 Subject: [PATCH 21/43] [Deps] update `resolve` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6f618421d0..1f958ec745 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "is-glob": "^4.0.3", "minimatch": "^3.0.4", "object.values": "^1.1.5", - "resolve": "^1.21.0", + "resolve": "^1.22.0", "tsconfig-paths": "^3.12.0" } } From b74013ded017c65ffe8d9a47e14935d2c605b2d3 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 23 Jan 2022 08:37:48 -0800 Subject: [PATCH 22/43] [Tests] export resolved parsers for cleaner comparison --- tests/src/core/getExports.js | 1 - tests/src/rules/default.js | 28 ++++++------- tests/src/rules/dynamic-import-chunkname.js | 6 +-- tests/src/rules/extensions.js | 4 +- tests/src/rules/max-dependencies.js | 8 ++-- tests/src/rules/named.js | 34 +++++++-------- tests/src/rules/namespace.js | 20 ++++----- tests/src/rules/newline-after-import.js | 20 ++++----- tests/src/rules/no-cycle.js | 24 +++++------ tests/src/rules/no-default-export.js | 12 +++--- tests/src/rules/no-duplicates.js | 8 ++-- tests/src/rules/no-dynamic-require.js | 30 ++++++------- tests/src/rules/no-extraneous-dependencies.js | 12 +++--- tests/src/rules/no-mutable-exports.js | 6 +-- tests/src/rules/no-named-as-default.js | 12 +++--- tests/src/rules/no-named-default.js | 8 ++-- tests/src/rules/no-named-export.js | 12 +++--- tests/src/rules/no-relative-parent-imports.js | 4 +- tests/src/rules/no-unresolved.js | 18 ++++---- tests/src/rules/no-unused-modules.js | 42 +++++++++---------- tests/src/rules/no-useless-path-segments.js | 14 +++---- tests/src/rules/order.js | 4 +- tests/src/rules/prefer-default-export.js | 12 +++--- tests/src/rules/unambiguous.js | 3 +- tests/src/utils.js | 25 +++++------ 25 files changed, 184 insertions(+), 183 deletions(-) diff --git a/tests/src/core/getExports.js b/tests/src/core/getExports.js index 604ae5cf28..867644bc19 100644 --- a/tests/src/core/getExports.js +++ b/tests/src/core/getExports.js @@ -343,7 +343,6 @@ describe('ExportMap', function () { }); context('alternate parsers', function () { - const configs = [ // ['string form', { 'typescript-eslint-parser': '.ts' }], ]; diff --git a/tests/src/rules/default.js b/tests/src/rules/default.js index c173f83542..9eab9a5a35 100644 --- a/tests/src/rules/default.js +++ b/tests/src/rules/default.js @@ -1,5 +1,5 @@ import path from 'path'; -import { test, testVersion, SYNTAX_CASES, getTSParsers } from '../utils'; +import { test, testVersion, SYNTAX_CASES, getTSParsers, parsers } from '../utils'; import { RuleTester } from 'eslint'; import { CASE_SENSITIVE_FS } from 'eslint-module-utils/resolve'; @@ -29,19 +29,19 @@ ruleTester.run('default', rule, { // es7 export syntax test({ code: 'export bar from "./bar"', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), test({ code: 'export { default as bar } from "./bar"' }), test({ code: 'export bar, { foo } from "./bar"', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), test({ code: 'export { default as bar, foo } from "./bar"' }), test({ code: 'export bar, * as names from "./bar"', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), // sanity check test({ code: 'export {a} from "./named-exports"' }), test({ code: 'import twofer from "./trampoline"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // jsx @@ -69,27 +69,27 @@ ruleTester.run('default', rule, { // from no-errors test({ code: "import Foo from './jsx/FooES7.js';", - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // #545: more ES7 cases test({ code: "import bar from './default-export-from.js';", - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: "import bar from './default-export-from-named.js';", - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: "import bar from './default-export-from-ignored.js';", settings: { 'import/ignore': ['common'] }, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: "export bar from './default-export-from-ignored.js';", settings: { 'import/ignore': ['common'] }, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // es2022: Arbitrary module namespace identifier names @@ -117,23 +117,23 @@ ruleTester.run('default', rule, { // es7 export syntax test({ code: 'export baz from "./named-exports"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: ['No default export found in imported module "./named-exports".'], }), test({ code: 'export baz, { bar } from "./named-exports"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: ['No default export found in imported module "./named-exports".'], }), test({ code: 'export baz, * as names from "./named-exports"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: ['No default export found in imported module "./named-exports".'], }), // exports default from a module with no default test({ code: 'import twofer from "./broken-trampoline"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: ['No default export found in imported module "./broken-trampoline".'], }), diff --git a/tests/src/rules/dynamic-import-chunkname.js b/tests/src/rules/dynamic-import-chunkname.js index a2ee55f13a..7e482cf03c 100644 --- a/tests/src/rules/dynamic-import-chunkname.js +++ b/tests/src/rules/dynamic-import-chunkname.js @@ -1,4 +1,4 @@ -import { SYNTAX_CASES, getTSParsers } from '../utils'; +import { SYNTAX_CASES, getTSParsers, parsers } from '../utils'; import { RuleTester } from 'eslint'; import semver from 'semver'; @@ -15,7 +15,7 @@ const pickyCommentOptions = [{ const multipleImportFunctionOptions = [{ importFunctions: ['dynamicImport', 'definitelyNotStaticImport'], }]; -const parser = require.resolve('babel-eslint'); +const parser = parsers.BABEL_OLD; const noLeadingCommentError = 'dynamic imports require a leading comment with the webpack chunkname'; const nonBlockCommentError = 'dynamic imports require a /* foo */ style comment, not a // foo comment'; @@ -495,7 +495,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { context('TypeScript', () => { getTSParsers().forEach((typescriptParser) => { - const nodeType = typescriptParser.includes('typescript-eslint-parser') || (typescriptParser.includes('@typescript-eslint/parser') && semver.satisfies(require('@typescript-eslint/parser/package.json').version, '^2')) + const nodeType = typescriptParser === parsers.TS_OLD || (typescriptParser === parsers.TS_NEW && semver.satisfies(require('@typescript-eslint/parser/package.json').version, '^2')) ? 'CallExpression' : 'ImportExpression'; diff --git a/tests/src/rules/extensions.js b/tests/src/rules/extensions.js index 93ebc28f8e..cf93fac9f4 100644 --- a/tests/src/rules/extensions.js +++ b/tests/src/rules/extensions.js @@ -1,6 +1,6 @@ import { RuleTester } from 'eslint'; import rule from 'rules/extensions'; -import { getTSParsers, test, testFilePath } from '../utils'; +import { getTSParsers, test, testFilePath, parsers } from '../utils'; const ruleTester = new RuleTester(); @@ -601,7 +601,7 @@ ruleTester.run('extensions', rule, { describe('TypeScript', () => { getTSParsers() // Type-only imports were added in TypeScript ESTree 2.23.0 - .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) + .filter((parser) => parser !== parsers.TS_OLD) .forEach((parser) => { ruleTester.run(`${parser}: extensions ignore type-only`, rule, { valid: [ diff --git a/tests/src/rules/max-dependencies.js b/tests/src/rules/max-dependencies.js index 6d80bbf046..982a4b427a 100644 --- a/tests/src/rules/max-dependencies.js +++ b/tests/src/rules/max-dependencies.js @@ -1,4 +1,4 @@ -import { test, getTSParsers } from '../utils'; +import { test, getTSParsers, parsers } from '../utils'; import { RuleTester } from 'eslint'; @@ -66,7 +66,7 @@ ruleTester.run('max-dependencies', rule, { test({ code: 'import type { x } from \'./foo\'; import type { y } from \'./bar\'', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ max: 1, }], @@ -77,7 +77,7 @@ ruleTester.run('max-dependencies', rule, { test({ code: 'import type { x } from \'./foo\'; import type { y } from \'./bar\'; import type { z } from \'./baz\'', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ max: 2, ignoreTypeImports: false, @@ -92,7 +92,7 @@ ruleTester.run('max-dependencies', rule, { describe('TypeScript', () => { getTSParsers() // Type-only imports were added in TypeScript ESTree 2.23.0 - .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) + .filter((parser) => parser !== parsers.TS_OLD) .forEach((parser) => { ruleTester.run(`max-dependencies (${parser.replace(process.cwd(), '.')})`, rule, { valid: [ diff --git a/tests/src/rules/named.js b/tests/src/rules/named.js index af2f9ce185..b5500a6d31 100644 --- a/tests/src/rules/named.js +++ b/tests/src/rules/named.js @@ -1,4 +1,4 @@ -import { test, SYNTAX_CASES, getTSParsers, testFilePath, testVersion } from '../utils'; +import { test, SYNTAX_CASES, getTSParsers, testFilePath, testVersion, parsers } from '../utils'; import { RuleTester } from 'eslint'; import { CASE_SENSITIVE_FS } from 'eslint-module-utils/resolve'; @@ -54,11 +54,11 @@ ruleTester.run('named', rule, { // es7 test({ code: 'export bar, { foo } from "./bar"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import { foo, bar } from "./named-trampoline"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // regression tests @@ -73,43 +73,43 @@ ruleTester.run('named', rule, { // should ignore imported/exported flow types, even if they don’t exist test({ code: 'import type { MissingType } from "./flowtypes"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import typeof { MissingType } from "./flowtypes"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import type { MyOpaqueType } from "./flowtypes"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import typeof { MyOpaqueType } from "./flowtypes"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import { type MyOpaqueType, MyClass } from "./flowtypes"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import { typeof MyOpaqueType, MyClass } from "./flowtypes"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import typeof MissingType from "./flowtypes"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import typeof * as MissingType from "./flowtypes"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'export type { MissingType } from "./flowtypes"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'export type { MyOpaqueType } from "./flowtypes"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // jsnext @@ -244,17 +244,17 @@ ruleTester.run('named', rule, { // es7 test({ code: 'export bar2, { bar } from "./bar"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: ["bar not found in './bar'"], }), test({ code: 'import { foo, bar, baz } from "./named-trampoline"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: ["baz not found in './named-trampoline'"], }), test({ code: 'import { baz } from "./broken-trampoline"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: ['baz not found via broken-trampoline.js -> named-exports.js'], }), @@ -294,7 +294,7 @@ ruleTester.run('named', rule, { test({ code: 'import { type MyOpaqueType, MyMissingClass } from "./flowtypes"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: ["MyMissingClass not found in './flowtypes'"], }), diff --git a/tests/src/rules/namespace.js b/tests/src/rules/namespace.js index 15ab133b1f..1465d21363 100644 --- a/tests/src/rules/namespace.js +++ b/tests/src/rules/namespace.js @@ -1,4 +1,4 @@ -import { test, SYNTAX_CASES, getTSParsers, testVersion, testFilePath } from '../utils'; +import { test, SYNTAX_CASES, getTSParsers, testVersion, testFilePath, parsers } from '../utils'; import { RuleTester } from 'eslint'; import flatMap from 'array.prototype.flatmap'; @@ -57,16 +57,16 @@ const valid = [ // es7 // ///////// test({ code: 'export * as names from "./named-exports"', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), test({ code: 'export defport, * as names from "./named-exports"', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), // non-existent is handled by no-unresolved test({ code: 'export * as names from "./does-not-exist"', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), test({ code: 'import * as Endpoints from "./issue-195/Endpoints"; console.log(Endpoints.Users)', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // respect hoisting @@ -81,11 +81,11 @@ const valid = [ test({ code: "import * as names from './default-export'; console.log(names.default)" }), test({ code: 'export * as names from "./default-export"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'export defport, * as names from "./default-export"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // #456: optionally ignore computed references @@ -103,7 +103,7 @@ const valid = [ }), test({ code: `import * as names from './named-exports'; const {a, b, ...rest} = names;`, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // #1144: should handle re-export CommonJS as namespace @@ -251,7 +251,7 @@ const invalid = [].concat( test({ code: 'import * as Endpoints from "./issue-195/Endpoints"; console.log(Endpoints.Foo)', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: ["'Foo' not found in imported namespace 'Endpoints'."], }), @@ -316,7 +316,7 @@ const invalid = [].concat( /////////////////////// // deep dereferences // ////////////////////// -;[['deep', require.resolve('espree')], ['deep-es7', require.resolve('babel-eslint')]].forEach(function ([folder, parser]) { // close over params +;[['deep', require.resolve('espree')], ['deep-es7', parsers.BABEL_OLD]].forEach(function ([folder, parser]) { // close over params valid.push( test({ parser, code: `import * as a from "./${folder}/a"; console.log(a.b.c.d.e)` }), test({ parser, code: `import { b } from "./${folder}/a"; console.log(b.c.d.e)` }), diff --git a/tests/src/rules/newline-after-import.js b/tests/src/rules/newline-after-import.js index a00e86900f..9ad18cbff8 100644 --- a/tests/src/rules/newline-after-import.js +++ b/tests/src/rules/newline-after-import.js @@ -1,7 +1,7 @@ import { RuleTester } from 'eslint'; import flatMap from 'array.prototype.flatmap'; -import { getTSParsers, testVersion } from '../utils'; +import { getTSParsers, parsers, testVersion } from '../utils'; const IMPORT_ERROR_MESSAGE = 'Expected 1 empty line after import statement not followed by another import.'; const IMPORT_ERROR_MESSAGE_MULTIPLE = (count) => { @@ -161,22 +161,22 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { class App {} `, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }, { code: `var foo = require('foo');\n\n@SomeDecorator(foo)\nclass Foo {}`, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }, { code : `// issue 1004\nimport foo from 'foo';\n\n@SomeDecorator(foo)\nexport default class Test {}`, parserOptions: { sourceType: 'module' }, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }, { code : `// issue 1004\nconst foo = require('foo');\n\n@SomeDecorator(foo)\nexport default class Test {}`, parserOptions: { sourceType: 'module' }, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }, ...flatMap(getTSParsers(), (parser) => [ { @@ -415,7 +415,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { message: IMPORT_ERROR_MESSAGE, } ], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }, { code: `var foo = require('foo');\n@SomeDecorator(foo)\nclass Foo {}`, @@ -426,7 +426,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { message: REQUIRE_ERROR_MESSAGE, } ], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }, { code: `// issue 10042\nimport foo from 'foo';\n@SomeDecorator(foo)\nexport default class Test {}`, @@ -437,7 +437,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { message: IMPORT_ERROR_MESSAGE, } ], parserOptions: { sourceType: 'module' }, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }, { code: `// issue 1004\nconst foo = require('foo');\n@SomeDecorator(foo)\nexport default class Test {}`, @@ -448,7 +448,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { message: REQUIRE_ERROR_MESSAGE, } ], parserOptions: { sourceType: 'module' }, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }, testVersion('>= 6', () => ({ code: ` @@ -472,7 +472,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { }, ], parserOptions: { sourceType: 'module' }, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, })) || [], ), }); diff --git a/tests/src/rules/no-cycle.js b/tests/src/rules/no-cycle.js index c4e3235e78..22e097dd2c 100644 --- a/tests/src/rules/no-cycle.js +++ b/tests/src/rules/no-cycle.js @@ -1,4 +1,4 @@ -import { test as _test, testFilePath } from '../utils'; +import { parsers, test as _test, testFilePath } from '../utils'; import { RuleTester } from 'eslint'; import flatMap from 'array.prototype.flatmap'; @@ -63,36 +63,36 @@ ruleTester.run('no-cycle', rule, { test({ code: `import("./${testDialect}/depth-two").then(function({ foo }) {})`, options: [{ maxDepth: 1 }], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: `import type { FooType } from "./${testDialect}/depth-one"`, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: `import type { FooType, BarType } from "./${testDialect}/depth-one"`, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), ]), test({ code: 'import { bar } from "./flow-types"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import { bar } from "./flow-types-only-importing-type"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import { bar } from "./flow-types-only-importing-multiple-types"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), ), invalid: [].concat( test({ code: 'import { bar } from "./flow-types-some-type-imports"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: [error(`Dependency cycle detected.`)], }), test({ @@ -166,17 +166,17 @@ ruleTester.run('no-cycle', rule, { test({ code: `import { bar } from "./${testDialect}/depth-three-indirect"`, errors: [error(`Dependency cycle via ./depth-two:1=>./depth-one:1`)], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: `import("./${testDialect}/depth-three-star")`, errors: [error(`Dependency cycle via ./depth-two:1=>./depth-one:1`)], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: `import("./${testDialect}/depth-three-indirect")`, errors: [error(`Dependency cycle via ./depth-two:1=>./depth-one:1`)], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: `import { foo } from "./${testDialect}/depth-two"`, @@ -192,7 +192,7 @@ ruleTester.run('no-cycle', rule, { test({ code: 'import { bar } from "./flow-types-depth-one"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: [error(`Dependency cycle via ./flow-types-depth-two:4=>./es6/depth-one:1`)], }), ), diff --git a/tests/src/rules/no-default-export.js b/tests/src/rules/no-default-export.js index 1d149421f9..6c1a85a1d5 100644 --- a/tests/src/rules/no-default-export.js +++ b/tests/src/rules/no-default-export.js @@ -1,4 +1,4 @@ -import { test, testVersion } from '../utils'; +import { parsers, test, testVersion } from '../utils'; import { RuleTester } from 'eslint'; @@ -58,7 +58,7 @@ ruleTester.run('no-default-export', rule, { }), test({ code: 'export { a, b } from "foo.js"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // no exports at all @@ -74,15 +74,15 @@ ruleTester.run('no-default-export', rule, { test({ code: `export type UserId = number;`, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'export foo from "foo.js"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: `export Memory, { MemoryValue } from './Memory'`, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), ], invalid: [].concat( @@ -154,7 +154,7 @@ ruleTester.run('no-default-export', rule, { }), test({ code: 'export default from "foo.js"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: [ { type: 'ExportNamedDeclaration', diff --git a/tests/src/rules/no-duplicates.js b/tests/src/rules/no-duplicates.js index ad39543f81..b481e6c823 100644 --- a/tests/src/rules/no-duplicates.js +++ b/tests/src/rules/no-duplicates.js @@ -1,5 +1,5 @@ import * as path from 'path'; -import { test as testUtil, getNonDefaultParsers } from '../utils'; +import { test as testUtil, getNonDefaultParsers, parsers } from '../utils'; import { RuleTester } from 'eslint'; import eslintPkg from 'eslint/package.json'; @@ -26,7 +26,7 @@ ruleTester.run('no-duplicates', rule, { // #225: ignore duplicate if is a flow type import test({ code: "import { x } from './foo'; import type { y } from './foo'", - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // #1107: Using different query strings that trigger different webpack loaders. @@ -107,7 +107,7 @@ ruleTester.run('no-duplicates', rule, { test({ code: "import type { x } from './foo'; import type { y } from './foo'", output: "import type { x , y } from './foo'; ", - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: ['\'./foo\' imported multiple times.', '\'./foo\' imported multiple times.'], }), @@ -418,7 +418,7 @@ import {x,y} from './foo' context('TypeScript', function () { getNonDefaultParsers() // Type-only imports were added in TypeScript ESTree 2.23.0 - .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) + .filter((parser) => parser !== parsers.TS_OLD) .forEach((parser) => { const parserConfig = { parser, diff --git a/tests/src/rules/no-dynamic-require.js b/tests/src/rules/no-dynamic-require.js index 4a70e7bc2b..368ec11935 100644 --- a/tests/src/rules/no-dynamic-require.js +++ b/tests/src/rules/no-dynamic-require.js @@ -1,4 +1,4 @@ -import { test } from '../utils'; +import { parsers, test } from '../utils'; import { RuleTester } from 'eslint'; @@ -30,54 +30,54 @@ ruleTester.run('no-dynamic-require', rule, { //dynamic import test({ code: 'import("foo")', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ esmodule: true }], }), test({ code: 'import(`foo`)', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ esmodule: true }], }), test({ code: 'import("./foo")', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ esmodule: true }], }), test({ code: 'import("@scope/foo")', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ esmodule: true }], }), test({ code: 'var foo = import("foo")', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ esmodule: true }], }), test({ code: 'var foo = import(`foo`)', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ esmodule: true }], }), test({ code: 'var foo = import("./foo")', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ esmodule: true }], }), test({ code: 'var foo = import("@scope/foo")', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ esmodule: true }], }), test({ code: 'import("../" + name)', errors: [dynamicImportError], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ esmodule: false }], }), test({ code: 'import(`../${name}`)', errors: [dynamicImportError], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), ], invalid: [ @@ -107,25 +107,25 @@ ruleTester.run('no-dynamic-require', rule, { test({ code: 'import("../" + name)', errors: [dynamicImportError], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ esmodule: true }], }), test({ code: 'import(`../${name}`)', errors: [dynamicImportError], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ esmodule: true }], }), test({ code: 'import(name)', errors: [dynamicImportError], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ esmodule: true }], }), test({ code: 'import(name())', errors: [dynamicImportError], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, options: [{ esmodule: true }], }), test({ diff --git a/tests/src/rules/no-extraneous-dependencies.js b/tests/src/rules/no-extraneous-dependencies.js index d4c9f6d7f5..d4e3886bed 100644 --- a/tests/src/rules/no-extraneous-dependencies.js +++ b/tests/src/rules/no-extraneous-dependencies.js @@ -1,4 +1,4 @@ -import { getTSParsers, test, testFilePath } from '../utils'; +import { getTSParsers, parsers, test, testFilePath } from '../utils'; import typescriptConfig from '../../../config/typescript'; import path from 'path'; import fs from 'fs'; @@ -82,7 +82,7 @@ ruleTester.run('no-extraneous-dependencies', rule, { test({ code: 'import type MyType from "myflowtyped";', options: [{ packageDir: packageDirWithFlowTyped }], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: ` @@ -90,7 +90,7 @@ ruleTester.run('no-extraneous-dependencies', rule, { import typeof TypeScriptModule from 'typescript'; `, options: [{ packageDir: packageDirWithFlowTyped }], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import react from "react";', @@ -398,7 +398,7 @@ ruleTester.run('no-extraneous-dependencies', rule, { describe('TypeScript', () => { getTSParsers() // Type-only imports were added in TypeScript ESTree 2.23.0 - .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) + .filter((parser) => parser !== parsers.TS_OLD) .forEach((parser) => { const parserConfig = { parser, @@ -433,12 +433,12 @@ typescriptRuleTester.run('no-extraneous-dependencies typescript type imports', r test({ code: 'import type MyType from "not-a-dependency";', filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import type { MyType } from "not-a-dependency";', filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), ], invalid: [ diff --git a/tests/src/rules/no-mutable-exports.js b/tests/src/rules/no-mutable-exports.js index e96b841b5c..1171443c4a 100644 --- a/tests/src/rules/no-mutable-exports.js +++ b/tests/src/rules/no-mutable-exports.js @@ -1,4 +1,4 @@ -import { test, testVersion } from '../utils'; +import { parsers, test, testVersion } from '../utils'; import { RuleTester } from 'eslint'; import rule from 'rules/no-mutable-exports'; @@ -25,11 +25,11 @@ ruleTester.run('no-mutable-exports', rule, { test({ code: 'class Counter {}\nexport default Counter' }), test({ code: 'class Counter {}\nexport { Counter as default }' }), test({ - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, code: 'export Something from "./something";', }), test({ - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, code: 'type Foo = {}\nexport type {Foo}', }), // es2022: Arbitrary module namespace identifier names diff --git a/tests/src/rules/no-named-as-default.js b/tests/src/rules/no-named-as-default.js index 129419350e..04ec28e615 100644 --- a/tests/src/rules/no-named-as-default.js +++ b/tests/src/rules/no-named-as-default.js @@ -1,4 +1,4 @@ -import { test, testVersion, SYNTAX_CASES } from '../utils'; +import { test, testVersion, SYNTAX_CASES, parsers } from '../utils'; import { RuleTester } from 'eslint'; const ruleTester = new RuleTester(); @@ -13,13 +13,13 @@ ruleTester.run('no-named-as-default', rule, { // es7 test({ code: 'export bar, { foo } from "./bar";', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), test({ code: 'export bar from "./bar";', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), // #566: don't false-positive on `default` itself test({ code: 'export default from "./bar";', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), // es2022: Arbitrary module namespae identifier names testVersion('>= 8.7', () => ({ @@ -45,13 +45,13 @@ ruleTester.run('no-named-as-default', rule, { // es7 test({ code: 'export foo from "./bar";', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: [ { message: 'Using exported name \'foo\' as identifier for default export.', type: 'ExportDefaultSpecifier' } ] }), test({ code: 'export foo, { foo as bar } from "./bar";', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: [ { message: 'Using exported name \'foo\' as identifier for default export.', type: 'ExportDefaultSpecifier' } ] }), diff --git a/tests/src/rules/no-named-default.js b/tests/src/rules/no-named-default.js index 1ed1502871..191c9c6ce9 100644 --- a/tests/src/rules/no-named-default.js +++ b/tests/src/rules/no-named-default.js @@ -1,4 +1,4 @@ -import { test, testVersion, SYNTAX_CASES } from '../utils'; +import { test, testVersion, SYNTAX_CASES, parsers } from '../utils'; import { RuleTester } from 'eslint'; const ruleTester = new RuleTester(); @@ -12,11 +12,11 @@ ruleTester.run('no-named-default', rule, { // Should ignore imported flow types test({ code: 'import { type default as Foo } from "./bar";', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import { typeof default as Foo } from "./bar";', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), ...SYNTAX_CASES, @@ -29,7 +29,7 @@ ruleTester.run('no-named-default', rule, { message: 'Use default import syntax to import \'default\'.', type: 'Identifier', }], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }),*/ test({ code: 'import { default as bar } from "./bar";', diff --git a/tests/src/rules/no-named-export.js b/tests/src/rules/no-named-export.js index 4d8978b35f..58b5da2f85 100644 --- a/tests/src/rules/no-named-export.js +++ b/tests/src/rules/no-named-export.js @@ -1,5 +1,5 @@ import { RuleTester } from 'eslint'; -import { test, testVersion } from '../utils'; +import { parsers, test, testVersion } from '../utils'; const ruleTester = new RuleTester(); const rule = require('rules/no-named-export'); @@ -14,7 +14,7 @@ ruleTester.run('no-named-export', rule, { }), test({ code: 'export default from "foo.js"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // no exports at all @@ -152,7 +152,7 @@ ruleTester.run('no-named-export', rule, { }), test({ code: 'export { a, b } from "foo.js"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: [{ type: 'ExportNamedDeclaration', message: 'Named exports are not allowed.', @@ -160,7 +160,7 @@ ruleTester.run('no-named-export', rule, { }), test({ code: `export type UserId = number;`, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: [{ type: 'ExportNamedDeclaration', message: 'Named exports are not allowed.', @@ -168,7 +168,7 @@ ruleTester.run('no-named-export', rule, { }), test({ code: 'export foo from "foo.js"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: [{ type: 'ExportNamedDeclaration', message: 'Named exports are not allowed.', @@ -176,7 +176,7 @@ ruleTester.run('no-named-export', rule, { }), test({ code: `export Memory, { MemoryValue } from './Memory'`, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: [{ type: 'ExportNamedDeclaration', message: 'Named exports are not allowed.', diff --git a/tests/src/rules/no-relative-parent-imports.js b/tests/src/rules/no-relative-parent-imports.js index d6a47ae373..3050498026 100644 --- a/tests/src/rules/no-relative-parent-imports.js +++ b/tests/src/rules/no-relative-parent-imports.js @@ -1,10 +1,10 @@ import { RuleTester } from 'eslint'; import rule from 'rules/no-relative-parent-imports'; -import { test as _test, testFilePath } from '../utils'; +import { parsers, test as _test, testFilePath } from '../utils'; const test = def => _test(Object.assign(def, { filename: testFilePath('./internal-modules/plugins/plugin2/index.js'), - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, })); const ruleTester = new RuleTester(); diff --git a/tests/src/rules/no-unresolved.js b/tests/src/rules/no-unresolved.js index 71efc128e3..c0252ad19d 100644 --- a/tests/src/rules/no-unresolved.js +++ b/tests/src/rules/no-unresolved.js @@ -1,6 +1,6 @@ -import * as path from 'path'; +import path from 'path'; -import { getTSParsers, test, SYNTAX_CASES, testVersion } from '../utils'; +import { getTSParsers, test, SYNTAX_CASES, testVersion, parsers } from '../utils'; import { CASE_SENSITIVE_FS } from 'eslint-module-utils/resolve'; @@ -30,7 +30,7 @@ function runResolverTests(resolver) { rest({ code: "import {someThing} from './test-module';" }), rest({ code: "import fs from 'fs';" }), rest({ code: "import('fs');", - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), // check with eslint parser testVersion('>= 7', () => rest({ @@ -46,9 +46,9 @@ function runResolverTests(resolver) { // stage 1 proposal for export symmetry, rest({ code: 'export * as bar from "./bar"', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), rest({ code: 'export bar from "./bar"', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), rest({ code: 'import foo from "./jsx/MyUnCoolComponent.jsx"' }), // commonjs setting @@ -127,7 +127,7 @@ function runResolverTests(resolver) { message: 'Unable to resolve path to module \'in-alternate-root\'.', type: 'Literal', }], - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), rest({ code: 'export { foo } from "./does-not-exist"', errors: ["Unable to resolve path to module './does-not-exist'."] }), @@ -148,11 +148,11 @@ function runResolverTests(resolver) { // export symmetry proposal rest({ code: 'export * as bar from "./does-not-exist"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: ["Unable to resolve path to module './does-not-exist'."], }), rest({ code: 'export bar from "./does-not-exist"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, errors: ["Unable to resolve path to module './does-not-exist'."], }), @@ -444,7 +444,7 @@ ruleTester.run('import() with built-in parser', rule, { context('TypeScript', () => { // Type-only imports were added in TypeScript ESTree 2.23.0 - getTSParsers().filter(x => x !== require.resolve('typescript-eslint-parser')).forEach((parser) => { + getTSParsers().filter(x => x !== parsers.TS_OLD).forEach((parser) => { ruleTester.run(`${parser}: no-unresolved ignore type-only`, rule, { valid: [ test({ diff --git a/tests/src/rules/no-unused-modules.js b/tests/src/rules/no-unused-modules.js index 8610ff4aa0..aa0e123c2b 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -1,4 +1,4 @@ -import { test, testVersion, testFilePath, getTSParsers } from '../utils'; +import { test, testVersion, testFilePath, getTSParsers, parsers } from '../utils'; import jsxConfig from '../../../config/react'; import typescriptConfig from '../../../config/typescript'; @@ -112,49 +112,49 @@ ruleTester.run('no-unused-modules', rule, { options: unusedExportsOptions, code: 'import { o2 } from "./file-o";export default () => 12', filename: testFilePath('./no-unused-modules/file-a.js'), - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ options: unusedExportsOptions, code: 'export const b = 2', filename: testFilePath('./no-unused-modules/file-b.js'), - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ options: unusedExportsOptions, code: 'const c1 = 3; function c2() { return 3 }; export { c1, c2 }', filename: testFilePath('./no-unused-modules/file-c.js'), - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ options: unusedExportsOptions, code: 'export function d() { return 4 }', filename: testFilePath('./no-unused-modules/file-d.js'), - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ options: unusedExportsOptions, code: 'export class q { q0() {} }', filename: testFilePath('./no-unused-modules/file-q.js'), - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ options: unusedExportsOptions, code: 'const e0 = 5; export { e0 as e }', filename: testFilePath('./no-unused-modules/file-e.js'), - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ options: unusedExportsOptions, code: 'const l0 = 5; const l = 10; export { l0 as l1, l }; export default () => {}', filename: testFilePath('./no-unused-modules/file-l.js'), - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ options: unusedExportsOptions, code: 'const o0 = 0; const o1 = 1; export { o0, o1 as o2 }; export default () => {}', filename: testFilePath('./no-unused-modules/file-o.js'), - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), ], invalid: [ @@ -263,7 +263,7 @@ describe('dynamic imports', () => { const d = 40 export default d `, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, filename: testFilePath('./no-unused-modules/exports-for-dynamic-js.js'), }), ], @@ -277,7 +277,7 @@ describe('dynamic imports', () => { const d = 40 export default d `, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, filename: testFilePath('./no-unused-modules/exports-for-dynamic-js-2.js'), errors: [ error(`exported declaration 'a' not used within other modules`), @@ -298,7 +298,7 @@ describe('dynamic imports', () => { const ts_d = 40 export default ts_d `, - parser: require.resolve('@typescript-eslint/parser'), + parser: parsers.TS_NEW, filename: testFilePath('./no-unused-modules/typescript/exports-for-dynamic-ts.ts'), }), ], @@ -1177,7 +1177,7 @@ describe('correctly work with JSX only files', () => { test({ options: unusedExportsJsxOptions, code: 'import a from "file-jsx-a";', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, filename: testFilePath('./no-unused-modules/jsx/file-jsx-a.jsx'), }), ], @@ -1185,7 +1185,7 @@ describe('correctly work with JSX only files', () => { test({ options: unusedExportsJsxOptions, code: `export const b = 2;`, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, filename: testFilePath('./no-unused-modules/jsx/file-jsx-b.jsx'), errors: [ error(`exported declaration 'b' not used within other modules`), @@ -1201,7 +1201,7 @@ describe('ignore flow types', () => { test({ options: unusedExportsOptions, code: 'import { type FooType, type FooInterface } from "./flow-2";', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, filename: testFilePath('./no-unused-modules/flow/flow-0.js'), }), test({ @@ -1210,13 +1210,13 @@ describe('ignore flow types', () => { export type FooType = string; export interface FooInterface {}; `, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, filename: testFilePath('./no-unused-modules/flow/flow-2.js'), }), test({ options: unusedExportsOptions, code: 'import type { FooType, FooInterface } from "./flow-4";', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, filename: testFilePath('./no-unused-modules/flow/flow-3.js'), }), test({ @@ -1225,7 +1225,7 @@ describe('ignore flow types', () => { export type FooType = string; export interface FooInterface {}; `, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, filename: testFilePath('./no-unused-modules/flow/flow-4.js'), }), test({ @@ -1234,7 +1234,7 @@ describe('ignore flow types', () => { export type Bar = number; export interface BarInterface {}; `, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, filename: testFilePath('./no-unused-modules/flow/flow-1.js'), }), ], @@ -1248,13 +1248,13 @@ describe('support (nested) destructuring assignment', () => { test({ options: unusedExportsOptions, code: 'import {a, b} from "./destructuring-b";', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, filename: testFilePath('./no-unused-modules/destructuring-a.js'), }), test({ options: unusedExportsOptions, code: 'const obj = {a: 1, dummy: {b: 2}}; export const {a, dummy: {b}} = obj;', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, filename: testFilePath('./no-unused-modules/destructuring-b.js'), }), ], diff --git a/tests/src/rules/no-useless-path-segments.js b/tests/src/rules/no-useless-path-segments.js index 21d9a5f704..f960953503 100644 --- a/tests/src/rules/no-useless-path-segments.js +++ b/tests/src/rules/no-useless-path-segments.js @@ -1,4 +1,4 @@ -import { test } from '../utils'; +import { parsers, test } from '../utils'; import { RuleTester } from 'eslint'; const ruleTester = new RuleTester(); @@ -29,11 +29,11 @@ function runResolverTests(resolver) { test({ code: 'import "./importType"', options: [{ noUselessIndex: true }] }), // ./importType.js does not exist test({ code: 'import(".")', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), test({ code: 'import("..")', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), test({ code: 'import("fs").then(function(fs) {})', - parser: require.resolve('babel-eslint') }), + parser: parsers.BABEL_OLD }), ], invalid: [ @@ -232,19 +232,19 @@ function runResolverTests(resolver) { code: 'import("./")', output: 'import(".")', errors: [ 'Useless path segments for "./", should be "."'], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import("../")', output: 'import("..")', errors: [ 'Useless path segments for "../", should be ".."'], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'import("./deep//a")', output: 'import("./deep/a")', errors: [ 'Useless path segments for "./deep//a", should be "./deep/a"'], - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), ], }); diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index 79426c4c43..e552c9a853 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -1,4 +1,4 @@ -import { test, getTSParsers, getNonDefaultParsers, testFilePath } from '../utils'; +import { test, getTSParsers, getNonDefaultParsers, testFilePath, parsers } from '../utils'; import { RuleTester } from 'eslint'; import eslintPkg from 'eslint/package.json'; @@ -2321,7 +2321,7 @@ ruleTester.run('order', rule, { context('TypeScript', function () { getNonDefaultParsers() // Type-only imports were added in TypeScript ESTree 2.23.0 - .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) + .filter((parser) => parser !== parsers.TS_OLD) .forEach((parser) => { const parserConfig = { parser, diff --git a/tests/src/rules/prefer-default-export.js b/tests/src/rules/prefer-default-export.js index d00a99668b..23b6e244ea 100644 --- a/tests/src/rules/prefer-default-export.js +++ b/tests/src/rules/prefer-default-export.js @@ -1,4 +1,4 @@ -import { test, testVersion, getNonDefaultParsers } from '../utils'; +import { test, testVersion, getNonDefaultParsers, parsers } from '../utils'; import { RuleTester } from 'eslint'; @@ -64,7 +64,7 @@ ruleTester.run('prefer-default-export', rule, { }), test({ code: `export Memory, { MemoryValue } from './Memory'`, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // no exports at all @@ -75,24 +75,24 @@ ruleTester.run('prefer-default-export', rule, { test({ code: `export type UserId = number;`, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // issue #653 test({ code: 'export default from "foo.js"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), test({ code: 'export { a, b } from "foo.js"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // ...SYNTAX_CASES, test({ code: ` export const [CounterProvider,, withCounter] = func();; `, - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, }), // es2022: Arbitrary module namespae identifier names testVersion('>= 8.7', () => ({ diff --git a/tests/src/rules/unambiguous.js b/tests/src/rules/unambiguous.js index 72e6b10828..8cef69625f 100644 --- a/tests/src/rules/unambiguous.js +++ b/tests/src/rules/unambiguous.js @@ -1,4 +1,5 @@ import { RuleTester } from 'eslint'; +import { parsers } from '../utils'; const ruleTester = new RuleTester(); const rule = require('rules/unambiguous'); @@ -38,7 +39,7 @@ ruleTester.run('unambiguous', rule, { }, { code: 'function x() {}; export * as y from "z"', - parser: require.resolve('babel-eslint'), + parser: parsers.BABEL_OLD, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { diff --git a/tests/src/utils.js b/tests/src/utils.js index 012c3a7c7b..49ba6b4702 100644 --- a/tests/src/utils.js +++ b/tests/src/utils.js @@ -5,24 +5,25 @@ import semver from 'semver'; // warms up the module cache. this import takes a while (>500ms) import 'babel-eslint'; +export const parsers = { + 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'), +}; + export function testFilePath(relativePath) { return path.join(process.cwd(), './tests/files', relativePath); } export function getTSParsers() { - const parsers = []; - if (semver.satisfies(eslintPkg.version, '>=4.0.0 <6.0.0')) { - parsers.push(require.resolve('typescript-eslint-parser')); - } - - if (semver.satisfies(eslintPkg.version, '>5.0.0')) { - parsers.push(require.resolve('@typescript-eslint/parser')); - } - return parsers; + return [ + parsers.TS_OLD, + parsers.TS_NEW, + ].filter(Boolean); } export function getNonDefaultParsers() { - return getTSParsers().concat(require.resolve('babel-eslint')); + return getTSParsers().concat(parsers.BABEL_OLD).filter(Boolean); } export const FILENAME = testFilePath('foo.js'); @@ -65,7 +66,7 @@ export const SYNTAX_CASES = [ test({ code: 'for (let [ foo, bar ] of baz) {}' }), test({ code: 'const { x, y } = bar' }), - test({ code: 'const { x, y, ...z } = bar', parser: require.resolve('babel-eslint') }), + test({ code: 'const { x, y, ...z } = bar', parser: parsers.BABEL_OLD }), // all the exports test({ code: 'let x; export { x }' }), @@ -73,7 +74,7 @@ export const SYNTAX_CASES = [ // not sure about these since they reference a file // test({ code: 'export { x } from "./y.js"'}), - // test({ code: 'export * as y from "./y.js"', parser: require.resolve('babel-eslint')}), + // test({ code: 'export * as y from "./y.js"', parser: parsers.BABEL_OLD}), test({ code: 'export const x = null' }), test({ code: 'export var x = null' }), From 624aa61e69c458d9f932e41ce4ffd189daf81033 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sat, 22 Jan 2022 20:00:49 +0900 Subject: [PATCH 23/43] [Tests] test on `@typescript-eslint/parser@5` --- .github/workflows/node-4+.yml | 5 ++++ package.json | 2 +- tests/dep-time-travel.sh | 2 ++ tests/src/rules/no-webpack-loader-syntax.js | 29 ++++++++++++--------- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/.github/workflows/node-4+.yml b/.github/workflows/node-4+.yml index 3af06b3bc3..6762bf0bbd 100644 --- a/.github/workflows/node-4+.yml +++ b/.github/workflows/node-4+.yml @@ -34,6 +34,11 @@ jobs: - 3 - 2 include: + - node-version: 'lts/*' + eslint: 7 + ts-parser: 4 + env: + TS_PARSER: 4 - node-version: 'lts/*' eslint: 7 ts-parser: 3 diff --git a/package.json b/package.json index 1f958ec745..1e003ec6b8 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "@angular-eslint/template-parser": "^13.0.1", "@eslint/import-test-order-redirect-scoped": "file:./tests/files/order-redirect-scoped", "@test-scope/some-module": "file:./tests/files/symlinked-module", - "@typescript-eslint/parser": "^2.23.0 || ^3.3.0 || ^4.29.3", + "@typescript-eslint/parser": "^2.23.0 || ^3.3.0 || ^4.29.3 || ^5.10.0", "array.prototype.flatmap": "^1.2.5", "babel-cli": "^6.26.0", "babel-core": "^6.26.3", diff --git a/tests/dep-time-travel.sh b/tests/dep-time-travel.sh index 82681b38f3..6ee500fe2c 100755 --- a/tests/dep-time-travel.sh +++ b/tests/dep-time-travel.sh @@ -21,6 +21,8 @@ elif [[ "$ESLINT_VERSION" -lt "5" ]]; then # completely remove the new TypeScrip npm uninstall --no-save @typescript-eslint/parser elif [[ "$TRAVIS_NODE_VERSION" -lt "10" ]]; then # TS parser 3 requires node 10+ npm i --no-save "@typescript-eslint/parser@3" +elif [[ "$TRAVIS_NODE_VERSION" -lt "12" ]]; then # TS parser 4 requires node 12+ + npm i --no-save "@typescript-eslint/parser@4" fi # use these alternate TypeScript dependencies for ESLint < v4 diff --git a/tests/src/rules/no-webpack-loader-syntax.js b/tests/src/rules/no-webpack-loader-syntax.js index a8aa0dd2b0..2b841e18a3 100644 --- a/tests/src/rules/no-webpack-loader-syntax.js +++ b/tests/src/rules/no-webpack-loader-syntax.js @@ -1,6 +1,7 @@ -import { test, getTSParsers } from '../utils'; +import { test, getTSParsers, parsers } from '../utils'; import { RuleTester } from 'eslint'; +import semver from 'semver'; const ruleTester = new RuleTester(); const rule = require('rules/no-webpack-loader-syntax'); @@ -82,16 +83,20 @@ context('TypeScript', function () { 'import/resolver': { 'eslint-import-resolver-typescript': true }, }, }; - ruleTester.run('no-webpack-loader-syntax', rule, { - valid: [ - test(Object.assign({ - code: 'import { foo } from\nalert()', - }, parserConfig)), - test(Object.assign({ - code: 'import foo from\nalert()', - }, parserConfig)), - ], - invalid: [], - }); + // @typescript-eslint/parser@5+ throw error for invalid module specifiers at parsing time. + // https://github.com/typescript-eslint/typescript-eslint/releases/tag/v5.0.0 + if (!(parser === parsers.TS_NEW && semver.satisfies(require('@typescript-eslint/parser/package.json').version, '>= 5'))) { + ruleTester.run('no-webpack-loader-syntax', rule, { + valid: [ + test(Object.assign({ + code: 'import { foo } from\nalert()', + }, parserConfig)), + test(Object.assign({ + code: 'import foo from\nalert()', + }, parserConfig)), + ], + invalid: [], + }); + } }); }); From 0ded887a6ac57913724799b403e021b0eef76f70 Mon Sep 17 00:00:00 2001 From: Stephan Troyer Date: Sat, 18 Dec 2021 21:45:11 +0100 Subject: [PATCH 24/43] [Docs] `no-unresolved`: Fix RegExp escaping in readme In the `ignore` example the backslash in `'\.img$'` was escaping the "." as a string character, not in the RegExp sense. --- CHANGELOG.md | 3 +++ docs/rules/no-unresolved.md | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 63de41cd73..2d1b0b4ac9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) - [Tests] `default`, `no-anonymous-default-export`, `no-mutable-exports`, `no-named-as-default-member`, `no-named-as-default`: add tests for arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) +- [Docs] [`no-unresolved`]: Fix RegExp escaping in readme ([#2332], thanks [@stephtr]) ## [2.25.4] - 2022-01-02 @@ -963,6 +964,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md [#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 [#2341]: https://github.com/import-js/eslint-plugin-import/pull/2341 [#2334]: https://github.com/import-js/eslint-plugin-import/pull/2334 @@ -1631,6 +1633,7 @@ for info on changes for earlier releases. [@spalger]: https://github.com/spalger [@st-sloth]: https://github.com/st-sloth [@stekycz]: https://github.com/stekycz +[@stephtr]: https://github.com/stephtr [@straub]: https://github.com/straub [@strawbrary]: https://github.com/strawbrary [@stropho]: https://github.com/stropho diff --git a/docs/rules/no-unresolved.md b/docs/rules/no-unresolved.md index 89d00b9301..1d18a3693a 100644 --- a/docs/rules/no-unresolved.md +++ b/docs/rules/no-unresolved.md @@ -60,7 +60,7 @@ This rule has its own ignore list, separate from [`import/ignore`]. This is beca To suppress errors from files that may not be properly resolved by your [resolver settings](../../README.md#resolver-plugins), you may add an `ignore` key with an array of `RegExp` pattern strings: ```js -/*eslint import/no-unresolved: [2, { ignore: ['\.img$'] }]*/ +/*eslint import/no-unresolved: [2, { ignore: ['\\.img$'] }]*/ import { x } from './mod' // may be reported, if not resolved to a module From a929394cb76c4550faf33d1eeca29e2d9f127962 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Wed, 26 Jan 2022 09:04:03 +0900 Subject: [PATCH 25/43] [Tests] export resolved `espree` path --- tests/src/utils.js | 1 + 1 file changed, 1 insertion(+) 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'), From 3d02b662dd3dcdf1196aa69a26464c753ef9dfd0 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Tue, 25 Jan 2022 22:21:00 +0900 Subject: [PATCH 26/43] [New] `no-dynamic-require`: support dynamic import with espree --- CHANGELOG.md | 2 + src/rules/no-dynamic-require.js | 13 +- tests/src/rules/no-dynamic-require.js | 206 +++++++++++++++++--------- 3 files changed, 146 insertions(+), 75 deletions(-) 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}`)', From fc98de271bcd512b6e2ce3211165e6c40b22cdde Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 27 Jan 2022 10:31:03 -0800 Subject: [PATCH 27/43] utils: v2.7.3 --- utils/CHANGELOG.md | 2 ++ utils/package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/utils/CHANGELOG.md b/utils/CHANGELOG.md index 7e36bb9fd6..193fc141e0 100644 --- a/utils/CHANGELOG.md +++ b/utils/CHANGELOG.md @@ -5,6 +5,8 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## Unreleased +## v2.7.3 - 2022-01-26 + ### Fixed - [Fix] `parse`: restore compatibility by making the return value `ast` again ([#2350], thanks [@ljharb]) diff --git a/utils/package.json b/utils/package.json index 97f95162f1..2e348d07f2 100644 --- a/utils/package.json +++ b/utils/package.json @@ -1,6 +1,6 @@ { "name": "eslint-module-utils", - "version": "2.7.2", + "version": "2.7.3", "description": "Core utilities to support eslint-plugin-import and other module-related plugins.", "engines": { "node": ">=4" From ad18a620d54fea111c8d385029a3c95770c22ddd Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 28 Jan 2022 14:36:58 -0800 Subject: [PATCH 28/43] [Fix] `default`: `typescript-eslint-parser`: avoid a crash on exporting as namespace - test on more versions of `typescript-eslint-parser` --- CHANGELOG.md | 4 ++++ package.json | 2 +- src/ExportMap.js | 2 +- tests/dep-time-travel.sh | 3 +++ tests/src/rules/default.js | 10 ++++++---- tests/src/rules/export.js | 17 +++++++++-------- tests/src/rules/newline-after-import.js | 18 ++++++++++-------- tests/src/rules/prefer-default-export.js | 18 ++++++++---------- 8 files changed, 42 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52777b296a..9d339757ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [`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]) + +### Fixed +- [`default`]: `typescript-eslint-parser`: avoid a crash on exporting as namespace (thanks [@ljharb]) + ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) - [Tests] `default`, `no-anonymous-default-export`, `no-mutable-exports`, `no-named-as-default-member`, `no-named-as-default`: add tests for arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) diff --git a/package.json b/package.json index 1e003ec6b8..2ebaf62402 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "semver": "^6.3.0", "sinon": "^2.4.1", "typescript": "^2.8.1 || ~3.9.5", - "typescript-eslint-parser": "^15 || ^22.0.0" + "typescript-eslint-parser": "^15 || ^20 || ^22" }, "peerDependencies": { "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" diff --git a/src/ExportMap.js b/src/ExportMap.js index 7425dce44a..d75c7ecd47 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -645,7 +645,7 @@ ExportMap.parse = function (path, content, context) { // This doesn't declare anything, but changes what's being exported. if (includes(exports, n.type)) { const exportedName = n.type === 'TSNamespaceExportDeclaration' - ? n.id.name + ? (n.id || n.name).name : (n.expression && n.expression.name || (n.expression.id && n.expression.id.name) || null); const declTypes = [ 'VariableDeclaration', diff --git a/tests/dep-time-travel.sh b/tests/dep-time-travel.sh index 6ee500fe2c..665ca1ccf1 100755 --- a/tests/dep-time-travel.sh +++ b/tests/dep-time-travel.sh @@ -32,6 +32,9 @@ if [[ "$ESLINT_VERSION" -lt "4" ]]; then echo "Downgrading TypeScript dependencies..." npm i --no-save typescript-eslint-parser@15 typescript@2.8.1 +elif [[ "$ESLINT_VERSION" -lt "7" ]]; then + echo "Downgrading TypeScript dependencies..." + npm i --no-save typescript-eslint-parser@20 fi # typescript-eslint-parser 1.1.1+ is not compatible with node 6 diff --git a/tests/src/rules/default.js b/tests/src/rules/default.js index 9eab9a5a35..eb2028c71a 100644 --- a/tests/src/rules/default.js +++ b/tests/src/rules/default.js @@ -1,6 +1,8 @@ import path from 'path'; import { test, testVersion, SYNTAX_CASES, getTSParsers, parsers } from '../utils'; import { RuleTester } from 'eslint'; +import semver from 'semver'; +import { version as tsEslintVersion } from 'typescript-eslint-parser/package.json'; import { CASE_SENSITIVE_FS } from 'eslint-module-utils/resolve'; @@ -165,7 +167,7 @@ if (!CASE_SENSITIVE_FS) { context('TypeScript', function () { getTSParsers().forEach((parser) => { ruleTester.run(`default`, rule, { - valid: [ + valid: [].concat( test({ code: `import foobar from "./typescript-default"`, parser, @@ -190,14 +192,14 @@ context('TypeScript', function () { 'import/resolver': { 'eslint-import-resolver-typescript': true }, }, }), - test({ + semver.satisfies(tsEslintVersion, '>= 22') ? test({ code: `import foobar from "./typescript-export-assign-mixed"`, parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, }, - }), + }) : [], test({ code: `import foobar from "./typescript-export-assign-default-reexport"`, parser, @@ -258,7 +260,7 @@ context('TypeScript', function () { 'import/resolver': { 'eslint-import-resolver-typescript': true }, }, }), - ], + ), invalid: [ test({ diff --git a/tests/src/rules/export.js b/tests/src/rules/export.js index d075aea6bd..e95efa0329 100644 --- a/tests/src/rules/export.js +++ b/tests/src/rules/export.js @@ -3,6 +3,7 @@ import { test, testFilePath, SYNTAX_CASES, getTSParsers, testVersion } from '../ import { RuleTester } from 'eslint'; import eslintPkg from 'eslint/package.json'; import semver from 'semver'; +import { version as tsEslintVersion } from 'typescript-eslint-parser/package.json'; const ruleTester = new RuleTester(); const rule = require('rules/export'); @@ -149,7 +150,7 @@ context('TypeScript', function () { }; ruleTester.run('export', rule, { - valid: [ + valid: [].concat( // type/value name clash test(Object.assign({ code: ` @@ -164,20 +165,20 @@ context('TypeScript', function () { `, }, parserConfig)), - test(Object.assign({ + semver.satisfies(tsEslintVersion, '>= 22') ? test(Object.assign({ code: ` export function fff(a: string); export function fff(a: number); `, - }, parserConfig)), + }, parserConfig)) : [], - test(Object.assign({ + semver.satisfies(tsEslintVersion, '>= 22') ? test(Object.assign({ code: ` export function fff(a: string); export function fff(a: number); export function fff(a: string|number) {}; `, - }, parserConfig)), + }, parserConfig)) : [], // namespace test(Object.assign({ @@ -224,7 +225,7 @@ context('TypeScript', function () { filename: testFilePath('typescript-d-ts/file-2.ts'), }, parserConfig)), - ...(semver.satisfies(eslintPkg.version, '< 6') ? [] : [ + (semver.satisfies(eslintPkg.version, '< 6') ? [] : [ test({ code: ` export * as A from './named-export-collision/a'; @@ -258,7 +259,7 @@ context('TypeScript', function () { `, }, parserConfig)), - ...(semver.satisfies(process.version, '< 8') && semver.satisfies(eslintPkg.version, '< 6') ? [] : test({ + (semver.satisfies(process.version, '< 8') && semver.satisfies(eslintPkg.version, '< 6') ? [] : test({ ...parserConfig, code: ` export * from './module'; @@ -269,7 +270,7 @@ context('TypeScript', function () { 'import/extensions': ['.js', '.ts', '.jsx'], }, })), - ], + ), invalid: [ // type/value name clash test(Object.assign({ diff --git a/tests/src/rules/newline-after-import.js b/tests/src/rules/newline-after-import.js index 9ad18cbff8..80cc076ced 100644 --- a/tests/src/rules/newline-after-import.js +++ b/tests/src/rules/newline-after-import.js @@ -1,5 +1,7 @@ import { RuleTester } from 'eslint'; import flatMap from 'array.prototype.flatmap'; +import semver from 'semver'; +import { version as tsEslintVersion } from 'typescript-eslint-parser/package.json'; import { getTSParsers, parsers, testVersion } from '../utils'; @@ -12,7 +14,7 @@ const REQUIRE_ERROR_MESSAGE = 'Expected 1 empty line after require statement not const ruleTester = new RuleTester(); ruleTester.run('newline-after-import', require('rules/newline-after-import'), { - valid: [ + valid: [].concat( `var path = require('path');\nvar foo = require('foo');\n`, `require('foo');`, `switch ('foo') { case 'bar': require('baz'); }`, @@ -178,7 +180,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { parserOptions: { sourceType: 'module' }, parser: parsers.BABEL_OLD, }, - ...flatMap(getTSParsers(), (parser) => [ + flatMap(getTSParsers(), (parser) => [].concat( { code: ` import { ExecaReturnValue } from 'execa'; @@ -213,14 +215,14 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { parser, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, - { + parser !== parsers.TS_OLD || semver.satisfies(tsEslintVersion, '>= 22') ? { code: ` export import a = obj;\nf(a); `, parser, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - }, - { + } : [], + parser !== parsers.TS_OLD || semver.satisfies(tsEslintVersion, '>= 22') ? { code: ` import { a } from "./a"; @@ -230,7 +232,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { }`, parser, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - }, + } : [], { code: ` import stub from './stub'; @@ -242,7 +244,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { parser, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, - ]), + )), { code: ` import stub from './stub'; @@ -253,7 +255,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { `, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, - ], + ), invalid: [].concat( { diff --git a/tests/src/rules/prefer-default-export.js b/tests/src/rules/prefer-default-export.js index 23b6e244ea..6a36f08bbf 100644 --- a/tests/src/rules/prefer-default-export.js +++ b/tests/src/rules/prefer-default-export.js @@ -1,6 +1,8 @@ import { test, testVersion, getNonDefaultParsers, parsers } from '../utils'; import { RuleTester } from 'eslint'; +import semver from 'semver'; +import { version as tsEslintVersion } from 'typescript-eslint-parser/package.json'; const ruleTester = new RuleTester(); const rule = require('../../../src/rules/prefer-default-export'); @@ -164,28 +166,24 @@ context('TypeScript', function () { }; ruleTester.run('prefer-default-export', rule, { - valid: [ + valid: [].concat( // Exporting types - test({ + semver.satisfies(tsEslintVersion, '>= 22') ? test({ code: ` export type foo = string; export type bar = number;`, ...parserConfig, - }), + }) : [], test({ code: ` export type foo = string; export type bar = number;`, ...parserConfig, }), - test({ - code: 'export type foo = string', - ...parserConfig, - }), - test({ + semver.satisfies(tsEslintVersion, '>= 22') ? test({ code: 'export type foo = string', ...parserConfig, - }), + }) : [], test({ code: 'export interface foo { bar: string; }', ...parserConfig, @@ -194,7 +192,7 @@ context('TypeScript', function () { code: 'export interface foo { bar: string; }; export function goo() {}', ...parserConfig, }), - ], + ), invalid: [], }); }); From 128d505642312cfeca4180a0ce702c23e982ca2e Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 28 Jan 2022 15:01:53 -0800 Subject: [PATCH 29/43] [Tests] handle eslint v8.8 adding `suppressedMessages` property --- tests/src/cli.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/src/cli.js b/tests/src/cli.js index 5b7e705b0e..e6afd8e441 100644 --- a/tests/src/cli.js +++ b/tests/src/cli.js @@ -104,6 +104,9 @@ describe('CLI regression tests', function () { fixableErrorCount: 0, fixableWarningCount: 0, source: results[0].source, // NewLine-characters might differ depending on git-settings + ...(semver.satisfies(eslintPkg.version, '>= 8.8') && { + suppressedMessages: [], + }), usedDeprecatedRules: results[0].usedDeprecatedRules, // we don't care about this one }, ], From eef7487368e9cba476c6095499f30d3c654631ce Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 30 Jan 2022 23:28:20 -0800 Subject: [PATCH 30/43] [Tests] consolidate eslint config --- .eslintrc | 20 ++++++++++++++++++++ memo-parser/.eslintrc.yml | 3 --- resolvers/webpack/.eslintrc | 9 --------- tests/.eslintrc | 9 --------- 4 files changed, 20 insertions(+), 21 deletions(-) delete mode 100644 memo-parser/.eslintrc.yml delete mode 100644 resolvers/webpack/.eslintrc delete mode 100644 tests/.eslintrc diff --git a/.eslintrc b/.eslintrc index 12fb8b7a23..2cbfd59cea 100644 --- a/.eslintrc +++ b/.eslintrc @@ -102,6 +102,16 @@ "es6": false, }, }, + { + "files": "resolvers/webpack/**", + "rules": { + "import/no-extraneous-dependencies": 1, + "no-console": 1, + }, + "env": { + "es6": true, + }, + }, { "files": [ "resolvers/*/test/**/*", @@ -127,5 +137,15 @@ "no-console": 1, }, }, + { + "files": "tests/**", + "env": { + "mocha": true, + }, + "rules": { + "max-len": 0, + "import/default": 0, + }, + }, ], } diff --git a/memo-parser/.eslintrc.yml b/memo-parser/.eslintrc.yml deleted file mode 100644 index e7e6b3d341..0000000000 --- a/memo-parser/.eslintrc.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -rules: - import/no-extraneous-dependencies: 1 diff --git a/resolvers/webpack/.eslintrc b/resolvers/webpack/.eslintrc deleted file mode 100644 index 544167c4bb..0000000000 --- a/resolvers/webpack/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "rules": { - "import/no-extraneous-dependencies": 1, - "no-console": 1, - }, - "env": { - "es6": true, - }, -} diff --git a/tests/.eslintrc b/tests/.eslintrc deleted file mode 100644 index 5720f3fcac..0000000000 --- a/tests/.eslintrc +++ /dev/null @@ -1,9 +0,0 @@ -{ - "env": { - "mocha": true, - }, - "rules": { - "max-len": 0, - "import/default": 0, - }, -} From 41d4500d83e969a0b24be837625f754b9eadada6 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 30 Jan 2022 23:28:57 -0800 Subject: [PATCH 31/43] [Deps] update `eslint-module-utils` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2ebaf62402..0462f9cb33 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,7 @@ "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", - "eslint-module-utils": "^2.7.2", + "eslint-module-utils": "^2.7.3", "has": "^1.0.3", "is-core-module": "^2.8.1", "is-glob": "^4.0.3", From ade33a49b1679a4ff7ba8fe472fab6a8a52fef4e Mon Sep 17 00:00:00 2001 From: Martin Garcia Date: Thu, 27 Jan 2022 17:10:07 +0100 Subject: [PATCH 32/43] [Fix] `export`: false positive for typescript namespace merging --- CHANGELOG.md | 5 +- src/rules/export.js | 64 +++++++++++++-- tests/src/rules/export.js | 166 +++++++++++++++++++++++++++++++++++++- 3 files changed, 221 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d339757ef..39401934c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [`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]) - ### Fixed - [`default`]: `typescript-eslint-parser`: avoid a crash on exporting as namespace (thanks [@ljharb]) +- [`export`]/TypeScript: false positive for typescript namespace merging ([#1964], thanks [@magarcia]) ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) @@ -1575,6 +1575,7 @@ for info on changes for earlier releases. [@ludofischer]: https://github.com/ludofischer [@lukeapage]: https://github.com/lukeapage [@lydell]: https://github.com/lydell +[@magarcia]: https://github.com/magarcia [@Mairu]: https://github.com/Mairu [@malykhinvi]: https://github.com/malykhinvi [@manovotny]: https://github.com/manovotny @@ -1665,4 +1666,4 @@ for info on changes for earlier releases. [@wtgtybhertgeghgtwtg]: https://github.com/wtgtybhertgeghgtwtg [@xpl]: https://github.com/xpl [@yordis]: https://github.com/yordis -[@zloirock]: https://github.com/zloirock \ No newline at end of file +[@zloirock]: https://github.com/zloirock diff --git a/src/rules/export.js b/src/rules/export.js index b9378f0910..3c998ec2c6 100644 --- a/src/rules/export.js +++ b/src/rules/export.js @@ -36,13 +36,59 @@ const tsTypePrefix = 'type:'; */ function isTypescriptFunctionOverloads(nodes) { const types = new Set(Array.from(nodes, node => node.parent.type)); - return ( - types.has('TSDeclareFunction') && - ( - types.size === 1 || - (types.size === 2 && types.has('FunctionDeclaration')) - ) - ); + return types.has('TSDeclareFunction') + && ( + types.size === 1 + || (types.size === 2 && types.has('FunctionDeclaration')) + ); +} + +/** + * Detect merging Namespaces with Classes, Functions, or Enums like: + * ```ts + * export class Foo { } + * export namespace Foo { } + * ``` + * @param {Set} nodes + * @returns {boolean} + */ +function isTypescriptNamespaceMerging(nodes) { + const types = new Set(Array.from(nodes, node => node.parent.type)); + const noNamespaceNodes = Array.from(nodes).filter((node) => node.parent.type !== 'TSModuleDeclaration'); + + return types.has('TSModuleDeclaration') + && ( + types.size === 1 + // Merging with functions + || (types.size === 2 && (types.has('FunctionDeclaration') || types.has('TSDeclareFunction'))) + || (types.size === 3 && types.has('FunctionDeclaration') && types.has('TSDeclareFunction')) + // Merging with classes or enums + || (types.size === 2 && (types.has('ClassDeclaration') || types.has('TSEnumDeclaration')) && noNamespaceNodes.length === 1) + ); +} + +/** + * Detect if a typescript namespace node should be reported as multiple export: + * ```ts + * export class Foo { } + * export function Foo(); + * export namespace Foo { } + * ``` + * @param {Object} node + * @param {Set} nodes + * @returns {boolean} + */ +function shouldSkipTypescriptNamespace(node, nodes) { + const types = new Set(Array.from(nodes, node => node.parent.type)); + + return !isTypescriptNamespaceMerging(nodes) + && node.parent.type === 'TSModuleDeclaration' + && ( + types.has('TSEnumDeclaration') + || types.has('ClassDeclaration') + || types.has('FunctionDeclaration') + || types.has('TSDeclareFunction') + ); } module.exports = { @@ -156,9 +202,11 @@ module.exports = { for (const [name, nodes] of named) { if (nodes.size <= 1) continue; - if (isTypescriptFunctionOverloads(nodes)) continue; + if (isTypescriptFunctionOverloads(nodes) || isTypescriptNamespaceMerging(nodes)) continue; for (const node of nodes) { + if (shouldSkipTypescriptNamespace(node, nodes)) continue; + if (name === 'default') { context.report(node, 'Multiple default exports.'); } else { diff --git a/tests/src/rules/export.js b/tests/src/rules/export.js index e95efa0329..53a7689a87 100644 --- a/tests/src/rules/export.js +++ b/tests/src/rules/export.js @@ -220,12 +220,50 @@ context('TypeScript', function () { } `, }, parserConfig)), + + semver.satisfies(eslintPkg.version, '>= 6') ? [ + test(Object.assign({ + code: ` + export class Foo { } + export namespace Foo { } + export namespace Foo { + export class Bar {} + } + `, + }, parserConfig)), + test(Object.assign({ + code: ` + export function Foo(); + export namespace Foo { } + `, + }, parserConfig)), + test(Object.assign({ + code: ` + export function Foo(a: string); + export namespace Foo { } + `, + }, parserConfig)), + test(Object.assign({ + code: ` + export function Foo(a: string); + export function Foo(a: number); + export namespace Foo { } + `, + }, parserConfig)), + test(Object.assign({ + code: ` + export enum Foo { } + export namespace Foo { } + `, + }, parserConfig)), + ] : [], + test(Object.assign({ code: 'export * from "./file1.ts"', filename: testFilePath('typescript-d-ts/file-2.ts'), }, parserConfig)), - (semver.satisfies(eslintPkg.version, '< 6') ? [] : [ + semver.satisfies(eslintPkg.version, '>= 6') ? [ test({ code: ` export * as A from './named-export-collision/a'; @@ -233,7 +271,7 @@ context('TypeScript', function () { `, parser, }), - ]), + ] : [], // Exports in ambient modules test(Object.assign({ @@ -271,7 +309,7 @@ context('TypeScript', function () { }, })), ), - invalid: [ + invalid: [].concat( // type/value name clash test(Object.assign({ code: ` @@ -361,6 +399,126 @@ context('TypeScript', function () { }, ], }, parserConfig)), + semver.satisfies(eslintPkg.version, '< 6') ? [] : [ + test(Object.assign({ + code: ` + export class Foo { } + export class Foo { } + export namespace Foo { } + `, + errors: [ + { + message: `Multiple exports of name 'Foo'.`, + line: 2, + }, + { + message: `Multiple exports of name 'Foo'.`, + line: 3, + }, + ], + }, parserConfig)), + test(Object.assign({ + code: ` + export enum Foo { } + export enum Foo { } + export namespace Foo { } + `, + errors: [ + { + message: `Multiple exports of name 'Foo'.`, + line: 2, + }, + { + message: `Multiple exports of name 'Foo'.`, + line: 3, + }, + ], + }, parserConfig)), + test(Object.assign({ + code: ` + export enum Foo { } + export class Foo { } + export namespace Foo { } + `, + errors: [ + { + message: `Multiple exports of name 'Foo'.`, + line: 2, + }, + { + message: `Multiple exports of name 'Foo'.`, + line: 3, + }, + ], + }, parserConfig)), + test(Object.assign({ + code: ` + export const Foo = 'bar'; + export class Foo { } + export namespace Foo { } + `, + errors: [ + { + message: `Multiple exports of name 'Foo'.`, + line: 2, + }, + { + message: `Multiple exports of name 'Foo'.`, + line: 3, + }, + ], + }, parserConfig)), + test(Object.assign({ + code: ` + export function Foo(); + export class Foo { } + export namespace Foo { } + `, + errors: [ + { + message: `Multiple exports of name 'Foo'.`, + line: 2, + }, + { + message: `Multiple exports of name 'Foo'.`, + line: 3, + }, + ], + }, parserConfig)), + test(Object.assign({ + code: ` + export const Foo = 'bar'; + export function Foo(); + export namespace Foo { } + `, + errors: [ + { + message: `Multiple exports of name 'Foo'.`, + line: 2, + }, + { + message: `Multiple exports of name 'Foo'.`, + line: 3, + }, + ], + }, parserConfig)), + test(Object.assign({ + code: ` + export const Foo = 'bar'; + export namespace Foo { } + `, + errors: [ + { + message: `Multiple exports of name 'Foo'.`, + line: 2, + }, + { + message: `Multiple exports of name 'Foo'.`, + line: 3, + }, + ], + }, parserConfig)), + ], // Exports in ambient modules test(Object.assign({ @@ -385,7 +543,7 @@ context('TypeScript', function () { }, ], }, parserConfig)), - ], + ), }); }); }); From 3603428771f3ad81370b85ec3d9a81b525bb65e6 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 1 Feb 2022 15:07:03 -0800 Subject: [PATCH 33/43] [Refactor] `export`: clean up rule, tests --- src/rules/export.js | 20 +++-- tests/src/rules/export.js | 177 +++++++++++++++++++++++--------------- 2 files changed, 118 insertions(+), 79 deletions(-) diff --git a/src/rules/export.js b/src/rules/export.js index 3c998ec2c6..4cae107402 100644 --- a/src/rules/export.js +++ b/src/rules/export.js @@ -131,15 +131,19 @@ module.exports = { } return { - 'ExportDefaultDeclaration': (node) => addNamed('default', node, getParent(node)), + ExportDefaultDeclaration(node) { + addNamed('default', node, getParent(node)); + }, - 'ExportSpecifier': (node) => addNamed( - node.exported.name || node.exported.value, - node.exported, - getParent(node.parent), - ), + ExportSpecifier(node) { + addNamed( + node.exported.name || node.exported.value, + node.exported, + getParent(node.parent), + ); + }, - 'ExportNamedDeclaration': function (node) { + ExportNamedDeclaration(node) { if (node.declaration == null) return; const parent = getParent(node); @@ -165,7 +169,7 @@ module.exports = { } }, - 'ExportAllDeclaration': function (node) { + ExportAllDeclaration(node) { if (node.source == null) return; // not sure if this is ever true // `export * as X from 'path'` does not conflict diff --git a/tests/src/rules/export.js b/tests/src/rules/export.js index 53a7689a87..5996e9fa3f 100644 --- a/tests/src/rules/export.js +++ b/tests/src/rules/export.js @@ -26,7 +26,7 @@ ruleTester.run('export', rule, { // #328: "export * from" does not export a default test({ code: 'export default foo; export * from "./bar"' }), - ...SYNTAX_CASES, + SYNTAX_CASES, test({ code: ` @@ -82,22 +82,31 @@ ruleTester.run('export', rule, { // }), test({ code: 'let foo; export { foo }; export * from "./export-all"', - errors: ['Multiple exports of name \'foo\'.', - 'Multiple exports of name \'foo\'.'], + errors: [ + 'Multiple exports of name \'foo\'.', + 'Multiple exports of name \'foo\'.', + ], }), - // test({ code: 'export * from "./default-export"' - // , errors: [{ message: 'No named exports found in module ' + - // '\'./default-export\'.' - // , type: 'Literal' }] }), + // test({ + // code: 'export * from "./default-export"', + // errors: [ + // { + // message: 'No named exports found in module \'./default-export\'.', + // type: 'Literal', + // }, + // ], + // }), // note: Espree bump to Acorn 4+ changed this test's error message. // `npm up` first if it's failing. test({ code: 'export * from "./malformed.js"', - errors: [{ - message: "Parse errors in imported module './malformed.js': 'return' outside of function (1:1)", - type: 'Literal', - }], + errors: [ + { + message: "Parse errors in imported module './malformed.js': 'return' outside of function (1:1)", + type: 'Literal', + }, + ], }), // test({ @@ -152,52 +161,58 @@ context('TypeScript', function () { ruleTester.run('export', rule, { valid: [].concat( // type/value name clash - test(Object.assign({ + test({ code: ` export const Foo = 1; export type Foo = number; `, - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export const Foo = 1; export interface Foo {} `, - }, parserConfig)), + ...parserConfig, + }), - semver.satisfies(tsEslintVersion, '>= 22') ? test(Object.assign({ + semver.satisfies(tsEslintVersion, '>= 22') ? test({ code: ` export function fff(a: string); export function fff(a: number); `, - }, parserConfig)) : [], + ...parserConfig, + }) : [], - semver.satisfies(tsEslintVersion, '>= 22') ? test(Object.assign({ + semver.satisfies(tsEslintVersion, '>= 22') ? test({ code: ` export function fff(a: string); export function fff(a: number); export function fff(a: string|number) {}; `, - }, parserConfig)) : [], + ...parserConfig, + }) : [], // namespace - test(Object.assign({ + test({ code: ` export const Bar = 1; export namespace Foo { export const Bar = 1; } `, - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export type Bar = string; export namespace Foo { export type Bar = string; } `, - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export const Bar = 1; export type Bar = string; @@ -206,8 +221,9 @@ context('TypeScript', function () { export type Bar = string; } `, - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export namespace Foo { export const Foo = 1; @@ -219,10 +235,10 @@ context('TypeScript', function () { } } `, - }, parserConfig)), - + ...parserConfig, + }), semver.satisfies(eslintPkg.version, '>= 6') ? [ - test(Object.assign({ + test({ code: ` export class Foo { } export namespace Foo { } @@ -230,38 +246,43 @@ context('TypeScript', function () { export class Bar {} } `, - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export function Foo(); export namespace Foo { } `, - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export function Foo(a: string); export namespace Foo { } `, - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export function Foo(a: string); export function Foo(a: number); export namespace Foo { } `, - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export enum Foo { } export namespace Foo { } `, - }, parserConfig)), + ...parserConfig, + }), ] : [], - - test(Object.assign({ + test({ code: 'export * from "./file1.ts"', filename: testFilePath('typescript-d-ts/file-2.ts'), - }, parserConfig)), + ...parserConfig, + }), semver.satisfies(eslintPkg.version, '>= 6') ? [ test({ @@ -274,7 +295,7 @@ context('TypeScript', function () { ] : [], // Exports in ambient modules - test(Object.assign({ + test({ code: ` declare module "a" { const Foo = 1; @@ -285,8 +306,9 @@ context('TypeScript', function () { export {Bar as default}; } `, - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` declare module "a" { const Foo = 1; @@ -295,9 +317,10 @@ context('TypeScript', function () { const Bar = 2; export {Bar as default}; `, - }, parserConfig)), + ...parserConfig, + }), - (semver.satisfies(process.version, '< 8') && semver.satisfies(eslintPkg.version, '< 6') ? [] : test({ + semver.satisfies(process.version, '< 8') && semver.satisfies(eslintPkg.version, '< 6') ? [] : test({ ...parserConfig, code: ` export * from './module'; @@ -307,11 +330,11 @@ context('TypeScript', function () { ...parserConfig.settings, 'import/extensions': ['.js', '.ts', '.jsx'], }, - })), + }), ), invalid: [].concat( // type/value name clash - test(Object.assign({ + test({ code: ` export type Foo = string; export type Foo = number; @@ -326,10 +349,11 @@ context('TypeScript', function () { line: 3, }, ], - }, parserConfig)), + ...parserConfig, + }), // namespace - test(Object.assign({ + test({ code: ` export const a = 1 export namespace Foo { @@ -347,8 +371,9 @@ context('TypeScript', function () { line: 5, }, ], - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` declare module 'foo' { const Foo = 1; @@ -366,8 +391,9 @@ context('TypeScript', function () { line: 5, }, ], - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export namespace Foo { export namespace Bar { @@ -398,9 +424,10 @@ context('TypeScript', function () { line: 9, }, ], - }, parserConfig)), + ...parserConfig, + }), semver.satisfies(eslintPkg.version, '< 6') ? [] : [ - test(Object.assign({ + test({ code: ` export class Foo { } export class Foo { } @@ -416,8 +443,9 @@ context('TypeScript', function () { line: 3, }, ], - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export enum Foo { } export enum Foo { } @@ -433,8 +461,9 @@ context('TypeScript', function () { line: 3, }, ], - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export enum Foo { } export class Foo { } @@ -450,8 +479,9 @@ context('TypeScript', function () { line: 3, }, ], - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export const Foo = 'bar'; export class Foo { } @@ -467,8 +497,9 @@ context('TypeScript', function () { line: 3, }, ], - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export function Foo(); export class Foo { } @@ -484,8 +515,9 @@ context('TypeScript', function () { line: 3, }, ], - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export const Foo = 'bar'; export function Foo(); @@ -501,8 +533,9 @@ context('TypeScript', function () { line: 3, }, ], - }, parserConfig)), - test(Object.assign({ + ...parserConfig, + }), + test({ code: ` export const Foo = 'bar'; export namespace Foo { } @@ -517,11 +550,12 @@ context('TypeScript', function () { line: 3, }, ], - }, parserConfig)), + ...parserConfig, + }), ], // Exports in ambient modules - test(Object.assign({ + test({ code: ` declare module "a" { const Foo = 1; @@ -542,7 +576,8 @@ context('TypeScript', function () { line: 9, }, ], - }, parserConfig)), + ...parserConfig, + }), ), }); }); From 0595a2f1c5b23680fbe5d234e4f30708ce1396e9 Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Sat, 29 Jan 2022 13:15:22 +0100 Subject: [PATCH 34/43] [Fix] `no-duplicates`: Ignore duplicate modules in different TypeScript module declarations Without this the `import/no-duplicates` rule reports imports of the same module inside different module declarations in the same file. It even autofixed them, which break the code. Closes #2273 --- CHANGELOG.md | 2 ++ src/rules/no-duplicates.js | 28 ++++++++++++++++++---------- tests/src/rules/no-duplicates.js | 14 ++++++++++++++ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39401934c9..c8361895b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Fixed - [`default`]: `typescript-eslint-parser`: avoid a crash on exporting as namespace (thanks [@ljharb]) - [`export`]/TypeScript: false positive for typescript namespace merging ([#1964], thanks [@magarcia]) +- [`no-duplicates`]: ignore duplicate modules in different TypeScript module declarations ([#2378], thanks [@remcohaszing]) ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) @@ -968,6 +969,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2378]: https://github.com/import-js/eslint-plugin-import/pull/2378 [#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 diff --git a/src/rules/no-duplicates.js b/src/rules/no-duplicates.js index 43c2c5b201..efd9583fbc 100644 --- a/src/rules/no-duplicates.js +++ b/src/rules/no-duplicates.js @@ -274,17 +274,23 @@ module.exports = { return defaultResolver(parts[1]) + '?' + parts[2]; }) : defaultResolver; - const imported = new Map(); - const nsImported = new Map(); - const defaultTypesImported = new Map(); - const namedTypesImported = new Map(); + const moduleMaps = new Map(); function getImportMap(n) { + if (!moduleMaps.has(n.parent)) { + moduleMaps.set(n.parent, { + imported: new Map(), + nsImported: new Map(), + defaultTypesImported: new Map(), + namedTypesImported: new Map(), + }); + } + const map = moduleMaps.get(n.parent); if (n.importKind === 'type') { - return n.specifiers.length > 0 && n.specifiers[0].type === 'ImportDefaultSpecifier' ? defaultTypesImported : namedTypesImported; + return n.specifiers.length > 0 && n.specifiers[0].type === 'ImportDefaultSpecifier' ? map.defaultTypesImported : map.namedTypesImported; } - return hasNamespace(n) ? nsImported : imported; + return hasNamespace(n) ? map.nsImported : map.imported; } return { @@ -301,10 +307,12 @@ module.exports = { }, 'Program:exit': function () { - checkImports(imported, context); - checkImports(nsImported, context); - checkImports(defaultTypesImported, context); - checkImports(namedTypesImported, context); + for (const map of moduleMaps.values()) { + checkImports(map.imported, context); + checkImports(map.nsImported, context); + checkImports(map.defaultTypesImported, context); + checkImports(map.namedTypesImported, context); + } }, }; }, diff --git a/tests/src/rules/no-duplicates.js b/tests/src/rules/no-duplicates.js index b481e6c823..cde41b3a07 100644 --- a/tests/src/rules/no-duplicates.js +++ b/tests/src/rules/no-duplicates.js @@ -454,6 +454,20 @@ context('TypeScript', function () { `, ...parserConfig, }), + test({ + code: ` + import type { Identifier } from 'module'; + + declare module 'module2' { + import type { Identifier } from 'module'; + } + + declare module 'module3' { + import type { Identifier } from 'module'; + } + `, + ...parserConfig, + }), ], invalid: [ test({ From 02ccbc1a30098822b7a6e7d84d8594b387495361 Mon Sep 17 00:00:00 2001 From: Emily Marigold Klassen Date: Thu, 3 Feb 2022 19:03:21 -0800 Subject: [PATCH 35/43] [New] `no-relative-packages`: add fixer --- CHANGELOG.md | 2 ++ docs/rules/no-relative-packages.md | 1 + src/rules/no-relative-packages.js | 8 ++++++++ tests/src/rules/no-relative-packages.js | 4 ++++ 4 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8361895b0..5c14fd3484 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,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]) +- [`no-relative-packages`]: add fixer ([#2381], thanks [@forivall]) ### Fixed - [`default`]: `typescript-eslint-parser`: avoid a crash on exporting as namespace (thanks [@ljharb]) @@ -969,6 +970,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2381]: https://github.com/import-js/eslint-plugin-import/pull/2381 [#2378]: https://github.com/import-js/eslint-plugin-import/pull/2378 [#2371]: https://github.com/import-js/eslint-plugin-import/pull/2371 [#2367]: https://github.com/import-js/eslint-plugin-import/pull/2367 diff --git a/docs/rules/no-relative-packages.md b/docs/rules/no-relative-packages.md index d5a0684932..a989c12a23 100644 --- a/docs/rules/no-relative-packages.md +++ b/docs/rules/no-relative-packages.md @@ -5,6 +5,7 @@ Use this rule to prevent importing packages through relative paths. It's useful in Yarn/Lerna workspaces, were it's possible to import a sibling package using `../package` relative path, while direct `package` is the correct one. ++(fixable) The `--fix` option on the [command line] automatically fixes problems reported by this rule. ### Examples diff --git a/src/rules/no-relative-packages.js b/src/rules/no-relative-packages.js index 17406e80eb..7bf1ce5cea 100644 --- a/src/rules/no-relative-packages.js +++ b/src/rules/no-relative-packages.js @@ -6,6 +6,11 @@ import moduleVisitor, { makeOptionsSchema } from 'eslint-module-utils/moduleVisi import importType from '../core/importType'; import docsUrl from '../docsUrl'; +/** @param {string} filePath */ +function toPosixPath(filePath) { + return filePath.replace(/\\/g, '/'); +} + function findNamedPackage(filePath) { const found = readPkgUp({ cwd: filePath }); if (found.pkg && !found.pkg.name) { @@ -42,6 +47,8 @@ function checkImportForRelativePackage(context, importPath, node) { context.report({ node, message: `Relative import from another package is not allowed. Use \`${properImport}\` instead of \`${importPath}\``, + fix: fixer => fixer.replaceText(node, JSON.stringify(toPosixPath(properImport))) + , }); } } @@ -52,6 +59,7 @@ module.exports = { docs: { url: docsUrl('no-relative-packages'), }, + fixable: 'code', schema: [makeOptionsSchema()], }, diff --git a/tests/src/rules/no-relative-packages.js b/tests/src/rules/no-relative-packages.js index 1a706387c0..2d27bcc91e 100644 --- a/tests/src/rules/no-relative-packages.js +++ b/tests/src/rules/no-relative-packages.js @@ -47,6 +47,7 @@ ruleTester.run('no-relative-packages', rule, { line: 1, column: 17, } ], + output: 'import foo from "package-named"', }), test({ code: 'import foo from "../package-named"', @@ -56,6 +57,7 @@ ruleTester.run('no-relative-packages', rule, { line: 1, column: 17, } ], + output: 'import foo from "package-named"', }), test({ code: 'import foo from "../package-scoped"', @@ -65,6 +67,7 @@ ruleTester.run('no-relative-packages', rule, { line: 1, column: 17, } ], + output: `import foo from "@scope/package-named"`, }), test({ code: 'import bar from "../bar"', @@ -74,6 +77,7 @@ ruleTester.run('no-relative-packages', rule, { line: 1, column: 17, } ], + output: `import bar from "eslint-plugin-import/tests/files/bar"`, }), ], }); From 35bd3a576c20005579cfbaa443a3a5f9d62250f6 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 20 Feb 2022 23:04:54 -0800 Subject: [PATCH 36/43] [Dev Deps] update `@angular-eslint/template-parser`, `chai` --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0462f9cb33..1ddf93c87a 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ }, "homepage": "https://github.com/import-js/eslint-plugin-import", "devDependencies": { - "@angular-eslint/template-parser": "^13.0.1", + "@angular-eslint/template-parser": "^13.1.0", "@eslint/import-test-order-redirect-scoped": "file:./tests/files/order-redirect-scoped", "@test-scope/some-module": "file:./tests/files/symlinked-module", "@typescript-eslint/parser": "^2.23.0 || ^3.3.0 || ^4.29.3 || ^5.10.0", @@ -67,7 +67,7 @@ "babel-preset-flow": "^6.23.0", "babel-register": "^6.26.0", "babylon": "^6.18.0", - "chai": "^4.3.4", + "chai": "^4.3.6", "cross-env": "^4.0.0", "escope": "^3.6.0", "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", From 00a4edee3501d65ada7f9f98f04ad2ada46ad86f Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 22 Feb 2022 13:18:12 -0800 Subject: [PATCH 37/43] [Deps] update `minimatch` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1ddf93c87a..9a55575143 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "has": "^1.0.3", "is-core-module": "^2.8.1", "is-glob": "^4.0.3", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "object.values": "^1.1.5", "resolve": "^1.22.0", "tsconfig-paths": "^3.12.0" From b0e6f7f48945e2533e96d513248bf3e54e0c1aac Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 22 Feb 2022 13:27:43 -0800 Subject: [PATCH 38/43] [Refactor] `namespace`: try to improve performance See #2340 --- CHANGELOG.md | 2 ++ src/rules/namespace.js | 79 +++++++++++++++++++++--------------------- 2 files changed, 41 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c14fd3484..84b1b4aa2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) - [Tests] `default`, `no-anonymous-default-export`, `no-mutable-exports`, `no-named-as-default-member`, `no-named-as-default`: add tests for arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) - [Docs] [`no-unresolved`]: Fix RegExp escaping in readme ([#2332], thanks [@stephtr]) +- [Refactor] `namespace`: try to improve performance ([#2340], thanks [@ljharb]) ## [2.25.4] - 2022-01-02 @@ -1255,6 +1256,7 @@ for info on changes for earlier releases. [#211]: https://github.com/import-js/eslint-plugin-import/pull/211 [#164]: https://github.com/import-js/eslint-plugin-import/pull/164 [#157]: https://github.com/import-js/eslint-plugin-import/pull/157 +[#2340]: https://github.com/import-js/eslint-plugin-import/issues/2340 [#2255]: https://github.com/import-js/eslint-plugin-import/issues/2255 [#2210]: https://github.com/import-js/eslint-plugin-import/issues/2210 [#2201]: https://github.com/import-js/eslint-plugin-import/issues/2201 diff --git a/src/rules/namespace.js b/src/rules/namespace.js index 8a7099df61..405c415cea 100644 --- a/src/rules/namespace.js +++ b/src/rules/namespace.js @@ -3,6 +3,44 @@ import Exports from '../ExportMap'; import importDeclaration from '../importDeclaration'; import docsUrl from '../docsUrl'; +function processBodyStatement(context, namespaces, declaration) { + if (declaration.type !== 'ImportDeclaration') return; + + if (declaration.specifiers.length === 0) return; + + const imports = Exports.get(declaration.source.value, context); + if (imports == null) return null; + + if (imports.errors.length > 0) { + imports.reportErrors(context, declaration); + return; + } + + declaration.specifiers.forEach((specifier) => { + switch (specifier.type) { + case 'ImportNamespaceSpecifier': + if (!imports.size) { + context.report( + specifier, + `No exported names found in module '${declaration.source.value}'.`, + ); + } + namespaces.set(specifier.local.name, imports); + break; + case 'ImportDefaultSpecifier': + case 'ImportSpecifier': { + const meta = imports.get( + // default to 'default' for default https://i.imgur.com/nj6qAWy.jpg + specifier.imported ? (specifier.imported.name || specifier.imported.value) : 'default', + ); + if (!meta || !meta.namespace) { break; } + namespaces.set(specifier.local.name, meta.namespace); + break; + } + } + }); +} + module.exports = { meta: { type: 'problem', @@ -41,44 +79,7 @@ module.exports = { return { // pick up all imports at body entry time, to properly respect hoisting Program({ body }) { - function processBodyStatement(declaration) { - if (declaration.type !== 'ImportDeclaration') return; - - if (declaration.specifiers.length === 0) return; - - const imports = Exports.get(declaration.source.value, context); - if (imports == null) return null; - - if (imports.errors.length) { - imports.reportErrors(context, declaration); - return; - } - - for (const specifier of declaration.specifiers) { - switch (specifier.type) { - case 'ImportNamespaceSpecifier': - if (!imports.size) { - context.report( - specifier, - `No exported names found in module '${declaration.source.value}'.`, - ); - } - namespaces.set(specifier.local.name, imports); - break; - case 'ImportDefaultSpecifier': - case 'ImportSpecifier': { - const meta = imports.get( - // default to 'default' for default https://i.imgur.com/nj6qAWy.jpg - specifier.imported ? (specifier.imported.name || specifier.imported.value) : 'default', - ); - if (!meta || !meta.namespace) { break; } - namespaces.set(specifier.local.name, meta.namespace); - break; - } - } - } - } - body.forEach(processBodyStatement); + body.forEach(x => processBodyStatement(context, namespaces, x)); }, // same as above, but does not add names to local map @@ -120,7 +121,6 @@ module.exports = { const namepath = [dereference.object.name]; // while property is namespace and parent is member expression, keep validating while (namespace instanceof Exports && dereference.type === 'MemberExpression') { - if (dereference.computed) { if (!allowComputed) { context.report( @@ -147,7 +147,6 @@ module.exports = { namespace = exported.namespace; dereference = dereference.parent; } - }, VariableDeclarator({ id, init }) { From 747d6dc5b905a718c89f10aad04cb2bcdbed7068 Mon Sep 17 00:00:00 2001 From: The Jared Wilcurt Date: Wed, 2 Mar 2022 18:26:46 -0500 Subject: [PATCH 39/43] [Docs] make rule doc titles consistent matches other docs pages now, easier for copy/pasting the rule --- CHANGELOG.md | 3 +++ docs/rules/dynamic-import-chunkname.md | 2 +- docs/rules/imports-first.md | 2 +- docs/rules/no-import-module-exports.md | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84b1b4aa2f..2c9357bf18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [Tests] `default`, `no-anonymous-default-export`, `no-mutable-exports`, `no-named-as-default-member`, `no-named-as-default`: add tests for arbitrary module namespace names ([#2358], thanks [@sosukesuzuki]) - [Docs] [`no-unresolved`]: Fix RegExp escaping in readme ([#2332], thanks [@stephtr]) - [Refactor] `namespace`: try to improve performance ([#2340], thanks [@ljharb]) +- [Docs] make rule doc titles consistent ([#2393], thanks [@TheJaredWilcurt]) ## [2.25.4] - 2022-01-02 @@ -971,6 +972,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2393]: https://github.com/import-js/eslint-plugin-import/pull/2393 [#2381]: https://github.com/import-js/eslint-plugin-import/pull/2381 [#2378]: https://github.com/import-js/eslint-plugin-import/pull/2378 [#2371]: https://github.com/import-js/eslint-plugin-import/pull/2371 @@ -1659,6 +1661,7 @@ for info on changes for earlier releases. [@Taranys]: https://github.com/Taranys [@taye]: https://github.com/taye [@TheCrueltySage]: https://github.com/TheCrueltySage +[@TheJaredWilcurt]: https://github.com/TheJaredWilcurt [@tihonove]: https://github.com/tihonove [@timkraut]: https://github.com/timkraut [@tizmagik]: https://github.com/tizmagik diff --git a/docs/rules/dynamic-import-chunkname.md b/docs/rules/dynamic-import-chunkname.md index d29c06bbaa..6b43074f19 100644 --- a/docs/rules/dynamic-import-chunkname.md +++ b/docs/rules/dynamic-import-chunkname.md @@ -1,4 +1,4 @@ -# dynamic imports require a leading comment with a webpackChunkName (dynamic-import-chunkname) +# import/dynamic-import-chunkname This rule reports any dynamic imports without a webpackChunkName specified in a leading block comment in the proper format. diff --git a/docs/rules/imports-first.md b/docs/rules/imports-first.md index 7dadffa684..4b90f04ea8 100644 --- a/docs/rules/imports-first.md +++ b/docs/rules/imports-first.md @@ -1,3 +1,3 @@ -# imports-first +# import/imports-first This rule was **deprecated** in eslint-plugin-import v2.0.0. Please use the corresponding rule [`first`](https://github.com/import-js/eslint-plugin-import/blob/HEAD/docs/rules/first.md). diff --git a/docs/rules/no-import-module-exports.md b/docs/rules/no-import-module-exports.md index 8131fd5f78..d658deb566 100644 --- a/docs/rules/no-import-module-exports.md +++ b/docs/rules/no-import-module-exports.md @@ -1,4 +1,4 @@ -# no-import-module-exports +# import/no-import-module-exports Reports the use of import declarations with CommonJS exports in any module except for the [main module](https://docs.npmjs.com/files/package.json#main). From 8b7000ec75968c1bd94f3099d67995a699286cf0 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 13 Mar 2022 13:37:07 -0700 Subject: [PATCH 40/43] [Fix] `no-unused-modules`: avoid a crash when processing re-exports Fixes #2388. --- CHANGELOG.md | 2 ++ src/rules/no-unused-modules.js | 6 ++++-- .../files/unused-modules-reexport-crash/src/App.tsx | 5 +++++ .../unused-modules-reexport-crash/src/index.tsx | 3 +++ .../src/magic/index.js | 1 + .../unused-modules-reexport-crash/src/magic/test.js | 7 +++++++ tests/src/rules/no-unused-modules.js | 13 ++++++++++++- 7 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 tests/files/unused-modules-reexport-crash/src/App.tsx create mode 100644 tests/files/unused-modules-reexport-crash/src/index.tsx create mode 100644 tests/files/unused-modules-reexport-crash/src/magic/index.js create mode 100644 tests/files/unused-modules-reexport-crash/src/magic/test.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c9357bf18..59ab25da95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [`default`]: `typescript-eslint-parser`: avoid a crash on exporting as namespace (thanks [@ljharb]) - [`export`]/TypeScript: false positive for typescript namespace merging ([#1964], thanks [@magarcia]) - [`no-duplicates`]: ignore duplicate modules in different TypeScript module declarations ([#2378], thanks [@remcohaszing]) +- [`no-unused-modules`]: avoid a crash when processing re-exports ([#2388], thanks [@ljharb]) ### Changed - [Tests] `no-nodejs-modules`: add tests for node protocol URL ([#2367], thanks [@sosukesuzuki]) @@ -973,6 +974,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md [#2393]: https://github.com/import-js/eslint-plugin-import/pull/2393 +[#2388]: https://github.com/import-js/eslint-plugin-import/pull/2388 [#2381]: https://github.com/import-js/eslint-plugin-import/pull/2381 [#2378]: https://github.com/import-js/eslint-plugin-import/pull/2378 [#2371]: https://github.com/import-js/eslint-plugin-import/pull/2371 diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index efc2169946..5feb319036 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -273,8 +273,10 @@ const prepareImportsAndExports = (srcFiles, context) => { exportAll.forEach((value, key) => { value.forEach(val => { const currentExports = exportList.get(val); - const currentExport = currentExports.get(EXPORT_ALL_DECLARATION); - currentExport.whereUsed.add(key); + if (currentExports) { + const currentExport = currentExports.get(EXPORT_ALL_DECLARATION); + currentExport.whereUsed.add(key); + } }); }); }; diff --git a/tests/files/unused-modules-reexport-crash/src/App.tsx b/tests/files/unused-modules-reexport-crash/src/App.tsx new file mode 100644 index 0000000000..c797a976cb --- /dev/null +++ b/tests/files/unused-modules-reexport-crash/src/App.tsx @@ -0,0 +1,5 @@ +import { hello } from './magic/test' + +hello(); + +export default function App() {}; diff --git a/tests/files/unused-modules-reexport-crash/src/index.tsx b/tests/files/unused-modules-reexport-crash/src/index.tsx new file mode 100644 index 0000000000..124b1745d2 --- /dev/null +++ b/tests/files/unused-modules-reexport-crash/src/index.tsx @@ -0,0 +1,3 @@ +import App from './App'; + +export const x = App \ No newline at end of file diff --git a/tests/files/unused-modules-reexport-crash/src/magic/index.js b/tests/files/unused-modules-reexport-crash/src/magic/index.js new file mode 100644 index 0000000000..ac3f46bb1b --- /dev/null +++ b/tests/files/unused-modules-reexport-crash/src/magic/index.js @@ -0,0 +1 @@ +export * from './test' diff --git a/tests/files/unused-modules-reexport-crash/src/magic/test.js b/tests/files/unused-modules-reexport-crash/src/magic/test.js new file mode 100644 index 0000000000..a6d74afd9b --- /dev/null +++ b/tests/files/unused-modules-reexport-crash/src/magic/test.js @@ -0,0 +1,7 @@ +export function hello() { + console.log('hello!!'); +} + +export function unused() { + console.log('im unused!!'); +} \ No newline at end of file diff --git a/tests/src/rules/no-unused-modules.js b/tests/src/rules/no-unused-modules.js index aa0e123c2b..38db2ef43d 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -105,7 +105,7 @@ ruleTester.run('no-unused-modules', rule, { }); -// tests for exports +// tests for exports ruleTester.run('no-unused-modules', rule, { valid: [ test({ @@ -301,6 +301,17 @@ describe('dynamic imports', () => { parser: parsers.TS_NEW, filename: testFilePath('./no-unused-modules/typescript/exports-for-dynamic-ts.ts'), }), + test({ + code: ` + import App from './App'; + `, + filename: testFilePath('./unused-modules-reexport-crash/src/index.tsx'), + parser: parsers.TS_NEW, + options: [{ + unusedExports: true, + ignoreExports: ['**/magic/**'], + }], + }), ], invalid: [ ], From 21304bdf0124be1db8e6b152b072661b50fa931c Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 13 Mar 2022 13:52:48 -0700 Subject: [PATCH 41/43] [Deps] update `tsconfig-paths` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9a55575143..186c5cb57e 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,6 @@ "minimatch": "^3.1.2", "object.values": "^1.1.5", "resolve": "^1.22.0", - "tsconfig-paths": "^3.12.0" + "tsconfig-paths": "^3.13.0" } } From 98bbb2cb30f90eaba91ff29bf1c1a21553886302 Mon Sep 17 00:00:00 2001 From: Seyyed MohammadMahdi Zamanian <76493524+MM25Zamanian@users.noreply.github.com> Date: Wed, 23 Mar 2022 02:23:33 +0430 Subject: [PATCH 42/43] [Docs] `order`: TS code examples should use TS code blocks --- CHANGELOG.md | 2 ++ docs/rules/order.md | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59ab25da95..7b97514f59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [Docs] [`no-unresolved`]: Fix RegExp escaping in readme ([#2332], thanks [@stephtr]) - [Refactor] `namespace`: try to improve performance ([#2340], thanks [@ljharb]) - [Docs] make rule doc titles consistent ([#2393], thanks [@TheJaredWilcurt]) +- [Docs] `order`: TS code examples should use TS code blocks ([#2411], thanks [@MM25Zamanian]) ## [2.25.4] - 2022-01-02 @@ -973,6 +974,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2411]: https://github.com/import-js/eslint-plugin-import/pull/2411 [#2393]: https://github.com/import-js/eslint-plugin-import/pull/2393 [#2388]: https://github.com/import-js/eslint-plugin-import/pull/2388 [#2381]: https://github.com/import-js/eslint-plugin-import/pull/2381 diff --git a/docs/rules/order.md b/docs/rules/order.md index 437467e244..f6e1ddbeb1 100644 --- a/docs/rules/order.md +++ b/docs/rules/order.md @@ -5,7 +5,7 @@ Enforce a convention in the order of `require()` / `import` statements. With the [`groups`](#groups-array) option set to `["builtin", "external", "internal", "parent", "sibling", "index", "object", "type"]` the order is as shown in the following example: -```js +```ts // 1. node "builtin" modules import fs from 'fs'; import path from 'path'; @@ -36,7 +36,7 @@ Statements using the ES6 `import` syntax must appear before any `require()` stat ## Fail -```js +```ts import _ from 'lodash'; import path from 'path'; // `path` import should occur before import of `lodash` @@ -54,7 +54,7 @@ import foo from './foo'; // `import` statements must be before `require` stateme ## Pass -```js +```ts import path from 'path'; import _ from 'lodash'; @@ -85,7 +85,7 @@ This rule supports the following options: How groups are defined, and the order to respect. `groups` must be an array of `string` or [`string`]. The only allowed `string`s are: `"builtin"`, `"external"`, `"internal"`, `"unknown"`, `"parent"`, `"sibling"`, `"index"`, `"object"`, `"type"`. The enforced order is the same as the order of each element in a group. Omitted types are implicitly grouped together as the last element. Example: -```js +```ts [ 'builtin', // Built-in types are first ['sibling', 'parent'], // Then sibling and parent types. They can be mingled together @@ -98,7 +98,7 @@ The default value is `["builtin", "external", "parent", "sibling", "index"]`. You can set the options like this: -```js +```ts "import/order": ["error", {"groups": ["index", "sibling", "parent", "internal", "external", "builtin", "object", "type"]}] ``` @@ -184,7 +184,7 @@ The default value is `"ignore"`. With the default group setting, the following will be invalid: -```js +```ts /* eslint import/order: ["error", {"newlines-between": "always"}] */ import fs from 'fs'; import path from 'path'; @@ -192,7 +192,7 @@ import index from './'; import sibling from './foo'; ``` -```js +```ts /* eslint import/order: ["error", {"newlines-between": "always-and-inside-groups"}] */ import fs from 'fs'; @@ -201,7 +201,7 @@ import index from './'; import sibling from './foo'; ``` -```js +```ts /* eslint import/order: ["error", {"newlines-between": "never"}] */ import fs from 'fs'; import path from 'path'; @@ -213,7 +213,7 @@ import sibling from './foo'; while those will be valid: -```js +```ts /* eslint import/order: ["error", {"newlines-between": "always"}] */ import fs from 'fs'; import path from 'path'; @@ -223,7 +223,7 @@ import index from './'; import sibling from './foo'; ``` -```js +```ts /* eslint import/order: ["error", {"newlines-between": "always-and-inside-groups"}] */ import fs from 'fs'; @@ -234,7 +234,7 @@ import index from './'; import sibling from './foo'; ``` -```js +```ts /* eslint import/order: ["error", {"newlines-between": "never"}] */ import fs from 'fs'; import path from 'path'; @@ -250,7 +250,7 @@ Sort the order within each group in alphabetical manner based on **import path** - `caseInsensitive`: use `true` to ignore case, and `false` to consider case (default: `false`). Example setting: -```js +```ts alphabetize: { order: 'asc', /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */ caseInsensitive: true /* ignore case. Options: [true, false] */ @@ -259,7 +259,7 @@ alphabetize: { This will fail the rule check: -```js +```ts /* eslint import/order: ["error", {"alphabetize": {"order": "asc", "caseInsensitive": true}}] */ import React, { PureComponent } from 'react'; import aTypes from 'prop-types'; @@ -270,7 +270,7 @@ import blist from 'BList'; While this will pass: -```js +```ts /* eslint import/order: ["error", {"alphabetize": {"order": "asc", "caseInsensitive": true}}] */ import blist from 'BList'; import * as classnames from 'classnames'; @@ -290,7 +290,7 @@ way that is safe. This will fail the rule check: -```js +```ts /* eslint import/order: ["error", {"warnOnUnassignedImports": true}] */ import fs from 'fs'; import './styles.css'; @@ -299,7 +299,7 @@ import path from 'path'; While this will pass: -```js +```ts /* eslint import/order: ["error", {"warnOnUnassignedImports": true}] */ import fs from 'fs'; import path from 'path'; From d8633c35ba7031eb49b2a36b27c82b289f8f85ce Mon Sep 17 00:00:00 2001 From: Kyle Holmberg Date: Mon, 28 Mar 2022 21:16:42 -0600 Subject: [PATCH 43/43] [Docs] `no-unresolved`: fix link --- CHANGELOG.md | 3 +++ docs/rules/no-unresolved.md | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b97514f59..3a8963de1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [Refactor] `namespace`: try to improve performance ([#2340], thanks [@ljharb]) - [Docs] make rule doc titles consistent ([#2393], thanks [@TheJaredWilcurt]) - [Docs] `order`: TS code examples should use TS code blocks ([#2411], thanks [@MM25Zamanian]) +- [Docs] `no-unresolved`: fix link ([#2417], thanks [@kylemh]) ## [2.25.4] - 2022-01-02 @@ -974,6 +975,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2417]: https://github.com/import-js/eslint-plugin-import/pull/2417 [#2411]: https://github.com/import-js/eslint-plugin-import/pull/2411 [#2393]: https://github.com/import-js/eslint-plugin-import/pull/2393 [#2388]: https://github.com/import-js/eslint-plugin-import/pull/2388 @@ -1571,6 +1573,7 @@ for info on changes for earlier releases. [@kmui2]: https://github.com/kmui2 [@knpwrs]: https://github.com/knpwrs [@KostyaZgara]: https://github.com/KostyaZgara +[@kylemh]: https://github.com/kylemh [@laysent]: https://github.com/laysent [@le0nik]: https://github.com/le0nik [@lemonmade]: https://github.com/lemonmade diff --git a/docs/rules/no-unresolved.md b/docs/rules/no-unresolved.md index 1d18a3693a..08522deb4c 100644 --- a/docs/rules/no-unresolved.md +++ b/docs/rules/no-unresolved.md @@ -98,7 +98,7 @@ If you're using a module bundler other than Node or Webpack, you may end up with ## Further Reading -- [Resolver plugins](../../README.md#resolver-plugins) +- [Resolver plugins](../../README.md#resolvers) - [Node resolver](https://npmjs.com/package/eslint-import-resolver-node) (default) - [Webpack resolver](https://npmjs.com/package/eslint-import-resolver-webpack) - [`import/ignore`] global setting