From defcf082e03f71af900e7e7d8abfe9a07f660446 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 17 Jan 2023 10:42:46 -0800 Subject: [PATCH 01/49] [Tests] `order`: add a passing test Closes #2662 --- tests/src/rules/order.js | 86 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 4 deletions(-) diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index b7d86dd93f..291dae33b0 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -1096,16 +1096,15 @@ ruleTester.run('order', rule, { // orderImportKind option that is not used test({ code: ` - import B from './B'; - import b from './b'; - `, + import B from './B'; + import b from './b'; + `, options: [ { 'alphabetize': { order: 'asc', orderImportKind: 'asc', 'caseInsensitive': true }, }, ], }), - ], invalid: [ // builtin before external module (require) @@ -3275,5 +3274,84 @@ flowRuleTester.run('order', rule, { message: '`./local/sub` typeof import should occur before import of `./local/sub`', }], }), + test({ + code: ` + import { cfg } from 'path/path/path/src/Cfg'; + import { l10n } from 'path/src/l10n'; + import { helpers } from 'path/path/path/helpers'; + import { tip } from 'path/path/tip'; + + import { controller } from '../../../../path/path/path/controller'; + import { component } from '../../../../path/path/path/component'; + `, + output: semver.satisfies(eslintPkg.version, '< 3') ? ` + import { cfg } from 'path/path/path/src/Cfg'; + import { tip } from 'path/path/tip'; + import { l10n } from 'path/src/l10n'; + import { helpers } from 'path/path/path/helpers'; + + import { component } from '../../../../path/path/path/component'; + import { controller } from '../../../../path/path/path/controller'; + ` : ` + import { helpers } from 'path/path/path/helpers'; + import { cfg } from 'path/path/path/src/Cfg'; + import { l10n } from 'path/src/l10n'; + import { tip } from 'path/path/tip'; + + import { component } from '../../../../path/path/path/component'; + import { controller } from '../../../../path/path/path/controller'; + `, + options: [ + { + 'groups': [ + ['builtin', 'external'], + 'internal', + ['sibling', 'parent'], + 'object', + 'type', + ], + 'pathGroups': [ + { + 'pattern': 'react', + 'group': 'builtin', + 'position': 'before', + 'patternOptions': { + 'matchBase': true, + }, + }, + { + 'pattern': '*.+(css|svg)', + 'group': 'type', + 'position': 'after', + 'patternOptions': { + 'matchBase': true, + }, + }, + ], + 'pathGroupsExcludedImportTypes': ['react'], + 'alphabetize': { + 'order': 'asc', + }, + 'newlines-between': 'always', + }, + ], + errors: [ + { + message: '`path/path/path/helpers` import should occur before import of `path/path/path/src/Cfg`', + line: 4, + column: 9, + }, + { + message: '`path/path/tip` import should occur before import of `path/src/l10n`', + line: 5, + column: 9, + }, + { + message: '`../../../../path/path/path/component` import should occur before import of `../../../../path/path/path/controller`', + line: 8, + column: 9, + }, + ], + }), ], }); From 766af5ff6c2e97cbe2a2a211729f7c129da3005d Mon Sep 17 00:00:00 2001 From: Mike Simmonds Date: Wed, 18 Jan 2023 22:29:38 +0000 Subject: [PATCH 02/49] [Docs] `no-duplicates`: fix example schema --- CHANGELOG.md | 5 +++++ docs/rules/no-duplicates.md | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d63f05558..894aa97b30 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 +- [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) + ## [2.27.5] - 2023-01-16 ### Fixed @@ -1381,6 +1384,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 +[#2684]: https://github.com/import-js/eslint-plugin-import/issues/2684 [#2674]: https://github.com/import-js/eslint-plugin-import/issues/2674 [#2668]: https://github.com/import-js/eslint-plugin-import/issues/2668 [#2666]: https://github.com/import-js/eslint-plugin-import/issues/2666 @@ -1800,6 +1804,7 @@ for info on changes for earlier releases. [@sheepsteak]: https://github.com/sheepsteak [@silviogutierrez]: https://github.com/silviogutierrez [@SimenB]: https://github.com/SimenB +[@simmo]: https://github.com/simmo [@sindresorhus]: https://github.com/sindresorhus [@singles]: https://github.com/singles [@skozin]: https://github.com/skozin diff --git a/docs/rules/no-duplicates.md b/docs/rules/no-duplicates.md index 553fbbcc34..5f3cfbd426 100644 --- a/docs/rules/no-duplicates.md +++ b/docs/rules/no-duplicates.md @@ -79,14 +79,14 @@ Config: -❌ Invalid `["error", "prefer-inline"]` +❌ Invalid `["error", {"prefer-inline": true}]` ```js import { AValue, type AType } from './mama-mia' import type { BType } from './mama-mia' ``` -✅ Valid with `["error", "prefer-inline"]` +✅ Valid with `["error", {"prefer-inline": true}]` ```js import { AValue, type AType, type BType } from './mama-mia' From 87a609689777203fe94a0b702fa92d387a52a3f4 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 27 Jan 2023 08:59:52 -0800 Subject: [PATCH 03/49] Revert "[Tests] update nvm in travis" This reverts commit 8b0fb989ae08161499a478ab23fb10b68e3cd828. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f57222a8ff..21a7070fb7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,6 @@ matrix: fast_finish: true before_install: - - 'curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash && . $NVM_DIR/nvm.sh' - 'nvm install-latest-npm' - 'NPM_CONFIG_LEGACY_PEER_DEPS=true npm install' - 'npm run copy-metafiles' From 50b3d23062e58b884d741b1e5b77d4feeea82747 Mon Sep 17 00:00:00 2001 From: Joe Portner <5295965+jportner@users.noreply.github.com> Date: Fri, 21 Oct 2022 11:59:00 -0400 Subject: [PATCH 04/49] [Fix] `no-duplicates`: remove duplicate identifiers in duplicate imports --- CHANGELOG.md | 5 +++++ src/rules/no-duplicates.js | 31 +++++++++++++++++++++++-------- tests/src/rules/no-duplicates.js | 31 +++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 894aa97b30..8186d87856 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## [Unreleased] +### Fixed +- [`no-duplicates`]: remove duplicate identifiers in duplicate imports ([#2577], thanks [@joe-matsec]) + ### Changed - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) @@ -1389,6 +1392,7 @@ for info on changes for earlier releases. [#2668]: https://github.com/import-js/eslint-plugin-import/issues/2668 [#2666]: https://github.com/import-js/eslint-plugin-import/issues/2666 [#2665]: https://github.com/import-js/eslint-plugin-import/issues/2665 +[#2577]: https://github.com/import-js/eslint-plugin-import/issues/2577 [#2444]: https://github.com/import-js/eslint-plugin-import/issues/2444 [#2412]: https://github.com/import-js/eslint-plugin-import/issues/2412 [#2392]: https://github.com/import-js/eslint-plugin-import/issues/2392 @@ -1703,6 +1707,7 @@ for info on changes for earlier releases. [@jimbolla]: https://github.com/jimbolla [@jkimbo]: https://github.com/jkimbo [@joaovieira]: https://github.com/joaovieira +[@joe-matsec]: https://github.com/joe-matsec [@johndevedu]: https://github.com/johndevedu [@johnthagen]: https://github.com/johnthagen [@jonboiser]: https://github.com/jonboiser diff --git a/src/rules/no-duplicates.js b/src/rules/no-duplicates.js index e2df4afdb4..15515e6757 100644 --- a/src/rules/no-duplicates.js +++ b/src/rules/no-duplicates.js @@ -79,8 +79,7 @@ function getFix(first, rest, sourceCode, context) { return { importNode: node, - text: sourceCode.text.slice(openBrace.range[1], closeBrace.range[0]), - hasTrailingComma: isPunctuator(sourceCode.getTokenBefore(closeBrace), ','), + identifiers: sourceCode.text.slice(openBrace.range[1], closeBrace.range[0]).split(','), // Split the text into separate identifiers (retaining any whitespace before or after) isEmpty: !hasSpecifiers(node), }; }) @@ -111,9 +110,15 @@ function getFix(first, rest, sourceCode, context) { closeBrace != null && isPunctuator(sourceCode.getTokenBefore(closeBrace), ','); const firstIsEmpty = !hasSpecifiers(first); + const firstExistingIdentifiers = firstIsEmpty + ? new Set() + : new Set(sourceCode.text.slice(openBrace.range[1], closeBrace.range[0]) + .split(',') + .map((x) => x.trim()), + ); const [specifiersText] = specifiers.reduce( - ([result, needsComma], specifier) => { + ([result, needsComma, existingIdentifiers], specifier) => { const isTypeSpecifier = specifier.importNode.importKind === 'type'; const preferInline = context.options[0] && context.options[0]['prefer-inline']; @@ -122,15 +127,25 @@ function getFix(first, rest, sourceCode, context) { throw new Error('Your version of TypeScript does not support inline type imports.'); } - const insertText = `${preferInline && isTypeSpecifier ? 'type ' : ''}${specifier.text}`; + // Add *only* the new identifiers that don't already exist, and track any new identifiers so we don't add them again in the next loop + const [specifierText, updatedExistingIdentifiers] = specifier.identifiers.reduce(([text, set], cur) => { + const trimmed = cur.trim(); // Trim whitespace before/after to compare to our set of existing identifiers + const curWithType = trimmed.length > 0 && preferInline && isTypeSpecifier ? `type ${cur}` : cur; + if (existingIdentifiers.has(trimmed)) { + return [text, set]; + } + return [text.length > 0 ? `${text},${curWithType}` : curWithType, set.add(trimmed)]; + }, ['', existingIdentifiers]); + return [ - needsComma && !specifier.isEmpty - ? `${result},${insertText}` - : `${result}${insertText}`, + needsComma && !specifier.isEmpty && specifierText.length > 0 + ? `${result},${specifierText}` + : `${result}${specifierText}`, specifier.isEmpty ? needsComma : true, + updatedExistingIdentifiers, ]; }, - ['', !firstHasTrailingComma && !firstIsEmpty], + ['', !firstHasTrailingComma && !firstIsEmpty, firstExistingIdentifiers], ); const fixes = []; diff --git a/tests/src/rules/no-duplicates.js b/tests/src/rules/no-duplicates.js index f8a27a743b..ac76c3070a 100644 --- a/tests/src/rules/no-duplicates.js +++ b/tests/src/rules/no-duplicates.js @@ -5,6 +5,7 @@ import jsxConfig from '../../../config/react'; import { RuleTester } from 'eslint'; import eslintPkg from 'eslint/package.json'; import semver from 'semver'; +import flatMap from 'array.prototype.flatmap'; const ruleTester = new RuleTester(); const rule = require('rules/no-duplicates'); @@ -130,6 +131,36 @@ ruleTester.run('no-duplicates', rule, { errors: ['\'./foo\' imported multiple times.', '\'./foo\' imported multiple times.'], }), + // These test cases use duplicate import identifiers, which causes a fatal parsing error using ESPREE (default) and TS_OLD. + ...flatMap([parsers.BABEL_OLD, parsers.TS_NEW], parser => { + if (!parser) return []; // TS_NEW is not always available + return [ + // #2347: duplicate identifiers should be removed + test({ + code: "import {a} from './foo'; import { a } from './foo'", + output: "import {a} from './foo'; ", + errors: ['\'./foo\' imported multiple times.', '\'./foo\' imported multiple times.'], + parser, + }), + + // #2347: duplicate identifiers should be removed + test({ + code: "import {a,b} from './foo'; import { b, c } from './foo'; import {b,c,d} from './foo'", + output: "import {a,b, c ,d} from './foo'; ", + errors: ['\'./foo\' imported multiple times.', '\'./foo\' imported multiple times.', '\'./foo\' imported multiple times.'], + parser, + }), + + // #2347: duplicate identifiers should be removed, but not if they are adjacent to comments + test({ + code: "import {a} from './foo'; import { a/*,b*/ } from './foo'", + output: "import {a, a/*,b*/ } from './foo'; ", + errors: ['\'./foo\' imported multiple times.', '\'./foo\' imported multiple times.'], + parser, + }), + ]; + }), + test({ code: "import {x} from './foo'; import {} from './foo'; import {/*c*/} from './foo'; import {y} from './foo'", output: "import {x/*c*/,y} from './foo'; ", From 808d504bffe29286a885475a424562969e9d4879 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 7 Feb 2023 10:21:05 -0800 Subject: [PATCH 05/49] [Fix] TypeScript config: fix resolver extension settings --- CHANGELOG.md | 1 + config/typescript.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8186d87856..1b1999bb26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Fixed - [`no-duplicates`]: remove duplicate identifiers in duplicate imports ([#2577], thanks [@joe-matsec]) +- TypeScript config: fix resolver extension settings (thanks [@gajus]) ### Changed - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) diff --git a/config/typescript.js b/config/typescript.js index ed03fb3f6c..59c3c42dae 100644 --- a/config/typescript.js +++ b/config/typescript.js @@ -16,7 +16,7 @@ module.exports = { '@typescript-eslint/parser': ['.ts', '.tsx'], }, 'import/resolver': { - 'node': { + 'typescript': { 'extensions': allExtensions, }, }, From d5fc8b670dc8e6903dbb7b0894452f60c03089f5 Mon Sep 17 00:00:00 2001 From: Devin Rhode Date: Tue, 31 Jan 2023 10:12:37 -0600 Subject: [PATCH 06/49] [Docs] `group-exports`: fix syntax highlighting These snippets didn't have any syntax highlighting: First chunk: https://share.cleanshot.com/spXGCRRGJkpBsLGLPk7k Second chunk: https://share.cleanshot.com/vhRsmDnxCd7PZfFfLLhh I searched codebase for any other ```flow js code blocks, but there were only these two from this file: https://share.cleanshot.com/xY7W9TJTXPrJ8ngtf6Kr --- CHANGELOG.md | 3 +++ docs/rules/group-exports.md | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b1999bb26..c16d2c9ffa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Changed - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) +- [Docs] [`group-exports`]: fix syntax highlighting ([#2699], thanks [@devinrhode2]) ## [2.27.5] - 2023-01-16 @@ -1064,6 +1065,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2699]: https://github.com/import-js/eslint-plugin-import/pull/2699 [#2664]: https://github.com/import-js/eslint-plugin-import/pull/2664 [#2613]: https://github.com/import-js/eslint-plugin-import/pull/2613 [#2608]: https://github.com/import-js/eslint-plugin-import/pull/2608 @@ -1654,6 +1656,7 @@ for info on changes for earlier releases. [@darkartur]: https://github.com/darkartur [@davidbonnet]: https://github.com/davidbonnet [@dbrewer5]: https://github.com/dbrewer5 +[@devinrhode2]: https://github.com/devinrhode2 [@devongovett]: https://github.com/devongovett [@dmnd]: https://github.com/dmnd [@duncanbeevers]: https://github.com/duncanbeevers diff --git a/docs/rules/group-exports.md b/docs/rules/group-exports.md index c5a23cd218..67e76de2fe 100644 --- a/docs/rules/group-exports.md +++ b/docs/rules/group-exports.md @@ -62,7 +62,7 @@ test.another = true module.exports = test ``` -```flow js +```ts const first = true; type firstType = boolean @@ -105,7 +105,7 @@ module.exports.first = true module.exports.second = true ``` -```flow js +```ts type firstType = boolean type secondType = any From 8f05399f3b13ea224b0dd5b6f137dc3ba5928574 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 12 Feb 2023 14:17:53 -0800 Subject: [PATCH 07/49] [Dev Deps] update `@angular-eslint/template-parser`, `chai`, `eslint-doc-generator`, `glob` --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 299cf52b0c..d543ec5a83 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ }, "homepage": "https://github.com/import-js/eslint-plugin-import", "devDependencies": { - "@angular-eslint/template-parser": "^13.2.1", + "@angular-eslint/template-parser": "^13.5.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", @@ -68,11 +68,11 @@ "babel-preset-flow": "^6.23.0", "babel-register": "^6.26.0", "babylon": "^6.18.0", - "chai": "^4.3.6", + "chai": "^4.3.7", "cross-env": "^4.0.0", "escope": "^3.6.0", "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", - "eslint-doc-generator": "^1.0.0", + "eslint-doc-generator": "^1.4.3", "eslint-import-resolver-node": "file:./resolvers/node", "eslint-import-resolver-typescript": "^1.0.2 || ^1.1.1", "eslint-import-resolver-webpack": "file:./resolvers/webpack", @@ -82,7 +82,7 @@ "eslint-plugin-import": "2.x", "eslint-plugin-json": "^2.1.2", "fs-copy-file-sync": "^1.1.1", - "glob": "^7.2.0", + "glob": "^7.2.3", "in-publish": "^2.0.1", "linklocal": "^2.8.2", "lodash.isarray": "^4.0.0", From 5680a1f8d41cd19f9c60d999a6fadf10994a0a64 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 25 Feb 2023 10:57:20 -0800 Subject: [PATCH 08/49] [Deps] update `tsconfig-paths` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d543ec5a83..3733e2d76f 100644 --- a/package.json +++ b/package.json @@ -114,6 +114,6 @@ "object.values": "^1.1.6", "resolve": "^1.22.1", "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" + "tsconfig-paths": "^3.14.2" } } From 6f12316ee7faa879fb046e04c8be8352f013a6f2 Mon Sep 17 00:00:00 2001 From: Brad Zacher Date: Thu, 6 Apr 2023 16:31:23 +0930 Subject: [PATCH 09/49] [Fix] `consistent-type-specifier-style`: fix accidental removal of comma in certain cases --- CHANGELOG.md | 2 ++ src/rules/consistent-type-specifier-style.js | 6 ++--- .../rules/consistent-type-specifier-style.js | 27 +++++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c16d2c9ffa..1866819b09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Fixed - [`no-duplicates`]: remove duplicate identifiers in duplicate imports ([#2577], thanks [@joe-matsec]) - TypeScript config: fix resolver extension settings (thanks [@gajus]) +- [`consistent-type-specifier-style`]: fix accidental removal of comma in certain cases ([#2754], thanks [@bradzacher]) ### Changed - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) @@ -1065,6 +1066,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2754]: https://github.com/import-js/eslint-plugin-import/pull/2754 [#2699]: https://github.com/import-js/eslint-plugin-import/pull/2699 [#2664]: https://github.com/import-js/eslint-plugin-import/pull/2664 [#2613]: https://github.com/import-js/eslint-plugin-import/pull/2613 diff --git a/src/rules/consistent-type-specifier-style.js b/src/rules/consistent-type-specifier-style.js index 869eea91ff..bb8fdf8498 100644 --- a/src/rules/consistent-type-specifier-style.js +++ b/src/rules/consistent-type-specifier-style.js @@ -7,9 +7,9 @@ function isComma(token) { function removeSpecifiers(fixes, fixer, sourceCode, specifiers) { for (const specifier of specifiers) { // remove the trailing comma - const comma = sourceCode.getTokenAfter(specifier, isComma); - if (comma) { - fixes.push(fixer.remove(comma)); + const token = sourceCode.getTokenAfter(specifier); + if (token && isComma(token)) { + fixes.push(fixer.remove(token)); } fixes.push(fixer.remove(specifier)); } diff --git a/tests/src/rules/consistent-type-specifier-style.js b/tests/src/rules/consistent-type-specifier-style.js index 440ef3aff5..7799238c32 100644 --- a/tests/src/rules/consistent-type-specifier-style.js +++ b/tests/src/rules/consistent-type-specifier-style.js @@ -168,6 +168,33 @@ const COMMON_TESTS = { type: 'ImportSpecifier', }], }, + // https://github.com/import-js/eslint-plugin-import/issues/2753 + { + code: `\ +import { Component, type ComponentProps } from "package-1"; +import { + Component1, + Component2, + Component3, + Component4, + Component5, +} from "package-2";`, + output: `\ +import { Component } from "package-1"; +import type {ComponentProps} from "package-1"; +import { + Component1, + Component2, + Component3, + Component4, + Component5, +} from "package-2";`, + options: ['prefer-top-level'], + errors: [{ + message: 'Prefer using a top-level type-only import instead of inline type specifiers.', + type: 'ImportSpecifier', + }], + }, // // prefer-inline From 6be042b559aecc50f3a5313f20023a2cc4879541 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 7 Apr 2023 22:22:40 -0700 Subject: [PATCH 10/49] [Tests] `no-extraneous-dependencies`: add passing test Closes #2718 --- tests/src/rules/no-extraneous-dependencies.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/src/rules/no-extraneous-dependencies.js b/tests/src/rules/no-extraneous-dependencies.js index c1018a9149..8815f63cd8 100644 --- a/tests/src/rules/no-extraneous-dependencies.js +++ b/tests/src/rules/no-extraneous-dependencies.js @@ -475,5 +475,14 @@ typescriptRuleTester.run('no-extraneous-dependencies typescript type imports', r message: `'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it`, }], }), + test({ + code: `import type { Foo } from 'not-a-dependency'`, + options: [{ includeTypes: true }], + filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), + parser: parsers.BABEL_OLD, + errors: [{ + message: `'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it`, + }], + }), ], }); From d2b10aec006d04680c8b36fbd4e4e325376a73c0 Mon Sep 17 00:00:00 2001 From: xM8WVqaG <44505331+xM8WVqaG@users.noreply.github.com> Date: Sat, 25 Mar 2023 14:49:48 +0000 Subject: [PATCH 11/49] [Docs] `extensions`: reference node ESM behavior This behavior largely didn't change despite the functionality becoming more widely adopted. Even though the document assumes CJS, It's probably worth including at least a footnote about the resolver behaving differently for ESM. See: https://github.com/import-js/eslint-plugin-import/issues/2043 --- CHANGELOG.md | 3 +++ docs/rules/extensions.md | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1866819b09..d10ceef814 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Changed - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) - [Docs] [`group-exports`]: fix syntax highlighting ([#2699], thanks [@devinrhode2]) +- [Docs] [`extensions`]: reference node ESM behavior ([#2748], thanks [@xM8WVqaG]) ## [2.27.5] - 2023-01-16 @@ -1067,6 +1068,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md [#2754]: https://github.com/import-js/eslint-plugin-import/pull/2754 +[#2748]: https://github.com/import-js/eslint-plugin-import/pull/2748 [#2699]: https://github.com/import-js/eslint-plugin-import/pull/2699 [#2664]: https://github.com/import-js/eslint-plugin-import/pull/2664 [#2613]: https://github.com/import-js/eslint-plugin-import/pull/2613 @@ -1853,6 +1855,7 @@ for info on changes for earlier releases. [@wKich]: https://github.com/wKich [@wschurman]: https://github.com/wschurman [@wtgtybhertgeghgtwtg]: https://github.com/wtgtybhertgeghgtwtg +[@xM8WVqaG]: https://github.com/xM8WVqaG [@xpl]: https://github.com/xpl [@yordis]: https://github.com/yordis [@zloirock]: https://github.com/zloirock diff --git a/docs/rules/extensions.md b/docs/rules/extensions.md index 9e78b8c70f..df4f341287 100644 --- a/docs/rules/extensions.md +++ b/docs/rules/extensions.md @@ -2,7 +2,7 @@ -Some file resolve algorithms allow you to omit the file extension within the import source path. For example the `node` resolver can resolve `./foo/bar` to the absolute path `/User/someone/foo/bar.js` because the `.js` extension is resolved automatically by default. Depending on the resolver you can configure more extensions to get resolved automatically. +Some file resolve algorithms allow you to omit the file extension within the import source path. For example the `node` resolver (which does not yet support ESM/`import`) can resolve `./foo/bar` to the absolute path `/User/someone/foo/bar.js` because the `.js` extension is resolved automatically by default in CJS. Depending on the resolver you can configure more extensions to get resolved automatically. In order to provide a consistent use of file extensions across your code base, this rule can enforce or disallow the use of certain file extensions. @@ -170,3 +170,5 @@ import foo from '@/foo'; ## When Not To Use It If you are not concerned about a consistent usage of file extension. + +In the future, when this rule supports native node ESM resolution, and the plugin is configured to use native rather than transpiled ESM (a config option that is not yet available) - setting this to `always` will have no effect. From 6cb9616d3fcc07b98b9efe3256b457f9d44a11cf Mon Sep 17 00:00:00 2001 From: Lukas Eipert Date: Sat, 8 Apr 2023 15:18:37 +0200 Subject: [PATCH 12/49] [utils] [perf] Performance of fullResolve While looking at a larger code base https://gitlab.com/gitlab-org/gitlab, I've came to realize that `fullResolve` takes a lot of CPU cycles, particularly the `hashObject` calls inside it. I applied the following patch locally to see how often this is called and how many unique hashes were produced: ```diff diff --git a/utils/resolve.js b/utils/resolve.js index 4a35c6a..3c28324 100644 --- a/utils/resolve.js +++ b/utils/resolve.js @@ -83,13 +83,28 @@ function relative(modulePath, sourceFile, settings) { return fullResolve(modulePath, sourceFile, settings).path; } +let prevSettings = null; +let nonEqualSettings = 0; +let totalCalls = 0; +let uniqueHashes = new Set(); function fullResolve(modulePath, sourceFile, settings) { // check if this is a bonus core module const coreSet = new Set(settings['import/core-modules']); if (coreSet.has(modulePath)) return { found: true, path: null }; const sourceDir = path.dirname(sourceFile); - const cacheKey = sourceDir + hashObject(settings).digest('hex') + modulePath; + + totalCalls+=1; + const hash = hashObject(settings).digest('hex'); + + if(prevSettings !== settings){ + uniqueHashes.add(hash); + prevSettings = settings; + nonEqualSettings+=1; + console.log(`fullResolve | Total calls:${totalCalls} | Non-Equal settings:${nonEqualSettings} | Unique hashes:${uniqueHashes.size} | dir:${sourceDir}`) + } + + const cacheKey = sourceDir + hash + modulePath; const cacheSettings = ModuleCache.getSettings(settings); ``` For our code base, `fullResolve` is called more than 570 thousand times. The simple in-equality `!==` code path is taken 1090 times. Actually only _four_ unique hashes are produced, meaning we only have four unique settings across our code base. I assume that a full object equality comparison might not be needed, and a simple object comparison with `!==` already would reduce the amount of `hashObject` calls by 570x. This is what is implemented in this commit. Time spend in `fullResolve` was reduced by ~38%: - Before: 17% (19.10s) of our total execution time - After: 11% (11.86s) of our total execution time The effect might even be more pronounced on machines that are slower when calculating `sha256` hashes or that have less memory, as the `hashObject` method tends to create loads of small strings which need to be garbage collected. --- utils/CHANGELOG.md | 5 +++++ utils/resolve.js | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/utils/CHANGELOG.md b/utils/CHANGELOG.md index 72fa611a14..7d2057dec2 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 +- Improve performance of `fullResolve` for large projects ([#2755], thanks [@leipert]) + ## v2.7.4 - 2022-08-11 ### Fixed @@ -123,6 +126,7 @@ Yanked due to critical issue with cache key resulting from #839. ### Fixed - `unambiguous.test()` regex is now properly in multiline mode +[#2755]: https://github.com/import-js/eslint-plugin-import/pull/2755 [#2523]: https://github.com/import-js/eslint-plugin-import/pull/2523 [#2431]: https://github.com/import-js/eslint-plugin-import/pull/2431 [#2350]: https://github.com/import-js/eslint-plugin-import/issues/2350 @@ -160,6 +164,7 @@ Yanked due to critical issue with cache key resulting from #839. [@iamnapo]: https://github.com/iamnapo [@JounQin]: https://github.com/JounQin [@kaiyoma]: https://github.com/kaiyoma +[@leipert]: https://github.com/leipert [@manuth]: https://github.com/manuth [@maxkomarychev]: https://github.com/maxkomarychev [@mgwalker]: https://github.com/mgwalker diff --git a/utils/resolve.js b/utils/resolve.js index 4a35c6a472..9d9dfa8439 100644 --- a/utils/resolve.js +++ b/utils/resolve.js @@ -83,13 +83,21 @@ function relative(modulePath, sourceFile, settings) { return fullResolve(modulePath, sourceFile, settings).path; } +let prevSettings = null; +let memoizedHash = ''; function fullResolve(modulePath, sourceFile, settings) { // check if this is a bonus core module const coreSet = new Set(settings['import/core-modules']); if (coreSet.has(modulePath)) return { found: true, path: null }; const sourceDir = path.dirname(sourceFile); - const cacheKey = sourceDir + hashObject(settings).digest('hex') + modulePath; + + if (prevSettings !== settings) { + memoizedHash = hashObject(settings).digest('hex'); + prevSettings = settings; + } + + const cacheKey = sourceDir + memoizedHash + modulePath; const cacheSettings = ModuleCache.getSettings(settings); From 3a22c3b05fa22c5c0e9db639007d79ff730678e8 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 11 Apr 2023 16:00:25 -0700 Subject: [PATCH 13/49] Revert "[Fix] TypeScript config: fix resolver extension settings" This reverts commit 808d504bffe29286a885475a424562969e9d4879, per https://github.com/import-js/eslint-plugin-import/commit/808d504bffe29286a885475a424562969e9d4879#commitcomment-107093802 --- CHANGELOG.md | 1 - config/typescript.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d10ceef814..01ab3f0819 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,6 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Fixed - [`no-duplicates`]: remove duplicate identifiers in duplicate imports ([#2577], thanks [@joe-matsec]) -- TypeScript config: fix resolver extension settings (thanks [@gajus]) - [`consistent-type-specifier-style`]: fix accidental removal of comma in certain cases ([#2754], thanks [@bradzacher]) ### Changed diff --git a/config/typescript.js b/config/typescript.js index 59c3c42dae..ed03fb3f6c 100644 --- a/config/typescript.js +++ b/config/typescript.js @@ -16,7 +16,7 @@ module.exports = { '@typescript-eslint/parser': ['.ts', '.tsx'], }, 'import/resolver': { - 'typescript': { + 'node': { 'extensions': allExtensions, }, }, From dc596a29bb3fe705396acc64f38d891b2ce52669 Mon Sep 17 00:00:00 2001 From: Daniel Martens Date: Fri, 10 Feb 2023 20:57:33 +0100 Subject: [PATCH 14/49] [utils] [new] `parse`: support flat config --- tests/src/core/parse.js | 59 ++++++++++++++++++++++++++++ tests/src/rules/no-unused-modules.js | 22 +++++++++++ utils/parse.js | 38 ++++++++++++------ 3 files changed, 108 insertions(+), 11 deletions(-) diff --git a/tests/src/core/parse.js b/tests/src/core/parse.js index 407070aa2f..4ab8370ed2 100644 --- a/tests/src/core/parse.js +++ b/tests/src/core/parse.js @@ -69,4 +69,63 @@ describe('parse(content, { settings, ecmaFeatures })', function () { expect(parse.bind(null, path, content, { settings: { 'import/parsers': { [parseStubParserPath]: [ '.js' ] } }, parserPath: null, parserOptions })).not.to.throw(Error); expect(parseSpy.callCount, 'custom parser to be called once').to.equal(1); }); + + it('throws on invalid languageOptions', function () { + expect(parse.bind(null, path, content, { settings: {}, parserPath: null, languageOptions: null })).to.throw(Error); + }); + + it('throws on non-object languageOptions.parser', function () { + expect(parse.bind(null, path, content, { settings: {}, parserPath: null, languageOptions: { parser: 'espree' } })).to.throw(Error); + }); + + it('throws on null languageOptions.parser', function () { + expect(parse.bind(null, path, content, { settings: {}, parserPath: null, languageOptions: { parser: null } })).to.throw(Error); + }); + + it('throws on empty languageOptions.parser', function () { + expect(parse.bind(null, path, content, { settings: {}, parserPath: null, languageOptions: { parser: {} } })).to.throw(Error); + }); + + it('throws on non-function languageOptions.parser.parse', function () { + expect(parse.bind(null, path, content, { settings: {}, parserPath: null, languageOptions: { parser: { parse: 'espree' } } })).to.throw(Error); + }); + + it('throws on non-function languageOptions.parser.parse', function () { + expect(parse.bind(null, path, content, { settings: {}, parserPath: null, languageOptions: { parser: { parseForESLint: 'espree' } } })).to.throw(Error); + }); + + it('requires only one of the parse methods', function () { + expect(parse.bind(null, path, content, { settings: {}, parserPath: null, languageOptions: { parser: { parseForESLint: () => ({ ast: {} }) } } })).not.to.throw(Error); + }); + + it('uses parse from languageOptions.parser', function () { + const parseSpy = sinon.spy(); + expect(parse.bind(null, path, content, { settings: {}, languageOptions: { parser: { parse: parseSpy } } })).not.to.throw(Error); + expect(parseSpy.callCount, 'passed parser to be called once').to.equal(1); + }); + + it('uses parseForESLint from languageOptions.parser', function () { + const parseSpy = sinon.spy(() => ({ ast: {} })); + expect(parse.bind(null, path, content, { settings: {}, languageOptions: { parser: { parseForESLint: parseSpy } } })).not.to.throw(Error); + expect(parseSpy.callCount, 'passed parser to be called once').to.equal(1); + }); + + it('prefers parsers specified in the settings over languageOptions.parser', () => { + const parseSpy = sinon.spy(); + parseStubParser.parse = parseSpy; + expect(parse.bind(null, path, content, { settings: { 'import/parsers': { [parseStubParserPath]: [ '.js' ] } }, parserPath: null, languageOptions: { parser: { parse() {} } } })).not.to.throw(Error); + expect(parseSpy.callCount, 'custom parser to be called once').to.equal(1); + }); + + it('ignores parser options from language options set to null', () => { + const parseSpy = sinon.spy(); + parseStubParser.parse = parseSpy; + expect(parse.bind(null, path, content, { settings: {}, parserPath: 'espree', languageOptions: { parserOptions: null }, parserOptions: { sourceType: 'module', ecmaVersion: 2015, ecmaFeatures: { jsx: true } } })).not.to.throw(Error); + }); + + it('prefers languageOptions.parserOptions over parserOptions', () => { + const parseSpy = sinon.spy(); + parseStubParser.parse = parseSpy; + expect(parse.bind(null, path, content, { settings: {}, parserPath: 'espree', languageOptions: { parserOptions: { sourceType: 'module', ecmaVersion: 2015, ecmaFeatures: { jsx: true } } }, parserOptions: { sourceType: 'script' } })).not.to.throw(Error); + }); }); diff --git a/tests/src/rules/no-unused-modules.js b/tests/src/rules/no-unused-modules.js index de169c65da..87714b599b 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -7,6 +7,11 @@ import fs from 'fs'; import eslintPkg from 'eslint/package.json'; import semver from 'semver'; +let FlatRuleTester; +try { + ({ FlatRuleTester } = require('eslint/use-at-your-own-risk')); +} catch (e) { /**/ } + // TODO: figure out why these tests fail in eslint 4 and 5 const isESLint4TODO = semver.satisfies(eslintPkg.version, '^4 || ^5'); @@ -1371,3 +1376,20 @@ describe('parser ignores prefixes like BOM and hashbang', () => { invalid: [], }); }); + +describe('supports flat eslint', { skip: !FlatRuleTester }, () => { + const flatRuleTester = new FlatRuleTester(); + flatRuleTester.run('no-unused-modules', rule, { + valid: [{ + options: unusedExportsOptions, + code: 'import { o2 } from "./file-o";export default () => 12', + filename: testFilePath('./no-unused-modules/file-a.js'), + }], + invalid: [{ + options: unusedExportsOptions, + code: 'export default () => 13', + filename: testFilePath('./no-unused-modules/file-f.js'), + errors: [error(`exported declaration 'default' not used within other modules`)], + }], + }); +}); diff --git a/utils/parse.js b/utils/parse.js index ac728ec5b2..dd0746aaa7 100644 --- a/utils/parse.js +++ b/utils/parse.js @@ -23,10 +23,10 @@ function keysFromParser(parserPath, parserInstance, parsedResult) { if (parsedResult && parsedResult.visitorKeys) { return parsedResult.visitorKeys; } - if (/.*espree.*/.test(parserPath)) { + if (typeof parserPath === 'string' && /.*espree.*/.test(parserPath)) { return parserInstance.VisitorKeys; } - if (/.*babel-eslint.*/.test(parserPath)) { + if (typeof parserPath === 'string' && /.*babel-eslint.*/.test(parserPath)) { return getBabelEslintVisitorKeys(parserPath); } return null; @@ -51,13 +51,13 @@ function transformHashbang(text) { } exports.default = function parse(path, content, context) { - if (context == null) throw new Error('need context to parse properly'); - let parserOptions = context.parserOptions; - const parserPath = getParserPath(path, context); + // ESLint in "flat" mode only sets context.languageOptions.parserOptions + let parserOptions = (context.languageOptions && context.languageOptions.parserOptions) || context.parserOptions; + const parserOrPath = getParser(path, context); - if (!parserPath) throw new Error('parserPath is required!'); + if (!parserOrPath) throw new Error('parserPath or languageOptions.parser is required!'); // hack: espree blows up with frozen options parserOptions = Object.assign({}, parserOptions); @@ -84,7 +84,7 @@ exports.default = function parse(path, content, context) { delete parserOptions.projects; // require the parser relative to the main module (i.e., ESLint) - const parser = moduleRequire(parserPath); + const parser = typeof parserOrPath === 'string' ? moduleRequire(parserOrPath) : parserOrPath; // replicate bom strip and hashbang transform of ESLint // https://github.com/eslint/eslint/blob/b93af98b3c417225a027cabc964c38e779adb945/lib/linter/linter.js#L779 @@ -95,7 +95,7 @@ exports.default = function parse(path, content, context) { try { const parserRaw = parser.parseForESLint(content, parserOptions); ast = parserRaw.ast; - return makeParseReturn(ast, keysFromParser(parserPath, parser, parserRaw)); + return makeParseReturn(ast, keysFromParser(parserOrPath, parser, parserRaw)); } catch (e) { console.warn(); console.warn('Error while parsing ' + parserOptions.filePath); @@ -104,18 +104,34 @@ exports.default = function parse(path, content, context) { if (!ast || typeof ast !== 'object') { console.warn( '`parseForESLint` from parser `' + - parserPath + + (typeof parserOrPath === 'string' ? parserOrPath : '`context.languageOptions.parser`') + // Can only be invalid for custom parser per imports/parser '` is invalid and will just be ignored' ); } else { - return makeParseReturn(ast, keysFromParser(parserPath, parser, undefined)); + return makeParseReturn(ast, keysFromParser(parserOrPath, parser, undefined)); } } const ast = parser.parse(content, parserOptions); - return makeParseReturn(ast, keysFromParser(parserPath, parser, undefined)); + return makeParseReturn(ast, keysFromParser(parserOrPath, parser, undefined)); }; +function getParser(path, context) { + const parserPath = getParserPath(path, context); + if (parserPath) { + return parserPath; + } + const isFlat = context.languageOptions + && context.languageOptions.parser + && typeof context.languageOptions.parser !== 'string' + && ( + typeof context.languageOptions.parser.parse === 'function' + || typeof context.languageOptions.parser.parseForESLint === 'function' + ); + + return isFlat ? context.languageOptions.parser : null; +} + function getParserPath(path, context) { const parsers = context.settings['import/parsers']; if (parsers != null) { From 0ae35c0efc02422f577bc0336ce9cef14b8339a0 Mon Sep 17 00:00:00 2001 From: Lukas Eipert Date: Wed, 12 Apr 2023 14:33:48 +0200 Subject: [PATCH 15/49] [perf] `ExportMap`: Improve ExportMap.for performance on larger codebases While looking at a larger code base https://gitlab.com/gitlab-org/gitlab, `ExportMap.for` seems to take a significant amount of time. More than half of it is spend on calculating hashes with `hashObject`. Digging a little deeper, it seems like we are calling it around 500 thousand times. Each iteration calculates the hash of a context object. This context object is created inside of the `childContext` function and consists of four parts: - `settings` -> an Object itself - `parserOptions` -> an Object itself - `parserPath` -> a String - `path` -> a String. Interestingly `settings`, `parserOptions` and `parserPath` rarely do change for us, so calculating their hashes on every iteration seems unnecessary. `hashObject` recursively calculates the hashes of each key / value pair. So instead of doing: ```js cacheKey = hashObject({settings, parserOptions, parserPath, path}) ``` We could also do: ```js cacheKey = parserPath + hashObject(parserOptions) + hashObject(settings) + path ``` This would be just as stable as before, although resulting in longer cache keys. `parserPath` and `path` would not need to be hashed, because they are strings and a single character change in them would result in a different cache key. Furthermore we can memoize the hashes of `parserOptions` and `settings`, in case they didn't change compared. The equality is checked with a simple `JSON.stringify`. We move this `cacheKey` calculation to `childContext`, adding the cache key to the `context` object. This way, we can fall back to the old calculation inside of `ExportMap.for`, as it is a public interface which consumers might be using. In our code base the results speak for itself: - 51.59s spent in `ExportMap.for`, 0ms spent in `childContext`. - 16.89s is spent in node:crypto/hash `update` (overall) - 41.02s spent in `ExportMap.for, 1.91s spent in `childContext`. - Almost no time spent in `hashObject`, actually all calls in our flame graph come from other code paths - 7.86s is spent in node:crypto/hash `update` (overall) So on this machine, and project, we are cutting the execution time of `ExportMap.for` in half. On machines, which are hashing slower, the effect might be more pronounced. Similarly machines with less memory, as the `hashObject` function creates a lot of tiny strings. One side-effect here could be, that the memoization is in-efficient if the `settings` or `parserOptions` change often. (I cannot think of such a scenario, but I am not that versed in the use cases of this plugin.) But even then, the overhead should mainly be the `JSON.stringify`. --- CHANGELOG.md | 3 +++ src/ExportMap.js | 21 +++++++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 01ab3f0819..bbfaa0fb98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Fixed - [`no-duplicates`]: remove duplicate identifiers in duplicate imports ([#2577], thanks [@joe-matsec]) - [`consistent-type-specifier-style`]: fix accidental removal of comma in certain cases ([#2754], thanks [@bradzacher]) +- [Perf] `ExportMap`: Improve `ExportMap.for` performance on larger codebases ([#2756], thanks [@leipert]) ### Changed - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) @@ -1066,6 +1067,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2756]: https://github.com/import-js/eslint-plugin-import/pull/2756 [#2754]: https://github.com/import-js/eslint-plugin-import/pull/2754 [#2748]: https://github.com/import-js/eslint-plugin-import/pull/2748 [#2699]: https://github.com/import-js/eslint-plugin-import/pull/2699 @@ -1737,6 +1739,7 @@ for info on changes for earlier releases. [@kylemh]: https://github.com/kylemh [@laysent]: https://github.com/laysent [@le0nik]: https://github.com/le0nik +[@leipert]: https://github.com/leipert [@lemonmade]: https://github.com/lemonmade [@lencioni]: https://github.com/lencioni [@leonardodino]: https://github.com/leonardodino diff --git a/src/ExportMap.js b/src/ExportMap.js index 7b8c883143..d31375c83d 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -305,7 +305,7 @@ ExportMap.get = function (source, context) { ExportMap.for = function (context) { const { path } = context; - const cacheKey = hashObject(context).digest('hex'); + const cacheKey = context.cacheKey || hashObject(context).digest('hex'); let exportMap = exportCache.get(cacheKey); // return cached ignore @@ -559,7 +559,7 @@ ExportMap.parse = function (path, content, context) { if (tsConfigInfo.tsConfigPath !== undefined) { // Projects not using TypeScript won't have `typescript` installed. if (!ts) { ts = require('typescript'); } // eslint-disable-line import/no-extraneous-dependencies - + const configFile = ts.readConfigFile(tsConfigInfo.tsConfigPath, ts.sys.readFile); return ts.parseJsonConfigFileContent( configFile.config, @@ -781,12 +781,29 @@ export function recursivePatternCapture(pattern, callback) { } } +let parserOptionsHash = ''; +let prevParserOptions = ''; +let settingsHash = ''; +let prevSettings = ''; /** * don't hold full context object in memory, just grab what we need. + * also calculate a cacheKey, where parts of the cacheKey hash are memoized */ function childContext(path, context) { const { settings, parserOptions, parserPath } = context; + + if (JSON.stringify(settings) !== prevSettings) { + settingsHash = hashObject({ settings }).digest('hex'); + prevSettings = JSON.stringify(settings); + } + + if (JSON.stringify(parserOptions) !== prevParserOptions) { + parserOptionsHash = hashObject({ parserOptions }).digest('hex'); + prevParserOptions = JSON.stringify(parserOptions); + } + return { + cacheKey: String(parserPath) + parserOptionsHash + settingsHash + String(path), settings, parserOptions, parserPath, From 97995673e02c3523559a4ae0792e1792d23064ec Mon Sep 17 00:00:00 2001 From: Andreas Deuschlinger Date: Tue, 14 Mar 2023 22:50:50 +0100 Subject: [PATCH 16/49] [Fix] `no-extraneous-dependencies`/TypeScript: do not error when importing inline type from dev dependencies --- CHANGELOG.md | 3 ++ docs/rules/no-extraneous-dependencies.md | 1 + src/rules/no-extraneous-dependencies.js | 7 ++++- tests/src/rules/no-extraneous-dependencies.js | 30 +++++++++++++++++-- 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbfaa0fb98..fe7f6771b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [`no-duplicates`]: remove duplicate identifiers in duplicate imports ([#2577], thanks [@joe-matsec]) - [`consistent-type-specifier-style`]: fix accidental removal of comma in certain cases ([#2754], thanks [@bradzacher]) - [Perf] `ExportMap`: Improve `ExportMap.for` performance on larger codebases ([#2756], thanks [@leipert]) +- [`no-extraneous-dependencies`]/TypeScript: do not error when importing inline type from dev dependencies ([#1820], thanks [@andyogo]) ### Changed - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) @@ -1070,6 +1071,7 @@ for info on changes for earlier releases. [#2756]: https://github.com/import-js/eslint-plugin-import/pull/2756 [#2754]: https://github.com/import-js/eslint-plugin-import/pull/2754 [#2748]: https://github.com/import-js/eslint-plugin-import/pull/2748 +[#2735]: https://github.com/import-js/eslint-plugin-import/pull/2735 [#2699]: https://github.com/import-js/eslint-plugin-import/pull/2699 [#2664]: https://github.com/import-js/eslint-plugin-import/pull/2664 [#2613]: https://github.com/import-js/eslint-plugin-import/pull/2613 @@ -1625,6 +1627,7 @@ for info on changes for earlier releases. [@alexgorbatchev]: https://github.com/alexgorbatchev [@andreubotella]: https://github.com/andreubotella [@AndrewLeedham]: https://github.com/AndrewLeedham +[@andyogo]: https://github.com/andyogo [@aravindet]: https://github.com/aravindet [@arvigeus]: https://github.com/arvigeus [@asapach]: https://github.com/asapach diff --git a/docs/rules/no-extraneous-dependencies.md b/docs/rules/no-extraneous-dependencies.md index 68cd4b154f..660875d1da 100644 --- a/docs/rules/no-extraneous-dependencies.md +++ b/docs/rules/no-extraneous-dependencies.md @@ -12,6 +12,7 @@ Modules have to be installed for this rule to work. This rule supports the following options: `devDependencies`: If set to `false`, then the rule will show an error when `devDependencies` are imported. Defaults to `true`. +Type imports are ignored by default. `optionalDependencies`: If set to `false`, then the rule will show an error when `optionalDependencies` are imported. Defaults to `true`. diff --git a/src/rules/no-extraneous-dependencies.js b/src/rules/no-extraneous-dependencies.js index d6437c2fd1..a149ca6599 100644 --- a/src/rules/no-extraneous-dependencies.js +++ b/src/rules/no-extraneous-dependencies.js @@ -178,7 +178,12 @@ function reportIfMissing(context, deps, depsOptions, node, name) { // Do not report when importing types unless option is enabled if ( !depsOptions.verifyTypeImports && - (node.importKind === 'type' || node.importKind === 'typeof') + (node.importKind === 'type' || node.importKind === 'typeof' || + ( + Array.isArray(node.specifiers) && + node.specifiers.length && + node.specifiers.every((specifier) => specifier.importKind === 'type' || specifier.importKind === 'typeof')) + ) ) { return; } diff --git a/tests/src/rules/no-extraneous-dependencies.js b/tests/src/rules/no-extraneous-dependencies.js index 8815f63cd8..84aa8bb35d 100644 --- a/tests/src/rules/no-extraneous-dependencies.js +++ b/tests/src/rules/no-extraneous-dependencies.js @@ -438,7 +438,7 @@ describe('TypeScript', () => { test(Object.assign({ code: 'import type T from "a";', - options: [{ + options: [{ packageDir: packageDirWithTypescriptDevDependencies, devDependencies: false, includeTypes: true, @@ -464,6 +464,16 @@ typescriptRuleTester.run('no-extraneous-dependencies typescript type imports', r filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), parser: parsers.BABEL_OLD, }), + test({ + code: 'import { type MyType } from "not-a-dependency";', + filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), + parser: parsers.BABEL_OLD, + }), + test({ + code: 'import { type MyType, type OtherType } from "not-a-dependency";', + filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), + parser: parsers.BABEL_OLD, + }), ], invalid: [ test({ @@ -476,7 +486,7 @@ typescriptRuleTester.run('no-extraneous-dependencies typescript type imports', r }], }), test({ - code: `import type { Foo } from 'not-a-dependency'`, + code: `import type { Foo } from 'not-a-dependency';`, options: [{ includeTypes: true }], filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), parser: parsers.BABEL_OLD, @@ -484,5 +494,21 @@ typescriptRuleTester.run('no-extraneous-dependencies typescript type imports', r message: `'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it`, }], }), + test({ + code: 'import Foo, { type MyType } from "not-a-dependency";', + filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), + parser: parsers.BABEL_OLD, + errors: [{ + message: `'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it`, + }], + }), + test({ + code: 'import { type MyType, Foo } from "not-a-dependency";', + filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), + parser: parsers.BABEL_OLD, + errors: [{ + message: `'not-a-dependency' should be listed in the project's dependencies. Run 'npm i -S not-a-dependency' to add it`, + }], + }), ], }); From 8c155baaca39dc78c5a3a63ff1bdd53b58f343e0 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 13 Apr 2023 16:20:26 -0700 Subject: [PATCH 17/49] [eslint] tighten up rules --- .eslintrc | 95 ++++++- config/typescript.js | 4 +- memo-parser/index.js | 4 +- resolvers/node/index.js | 9 +- resolvers/node/test/packageMains.js | 1 - resolvers/node/test/paths.js | 2 - resolvers/webpack/index.js | 20 +- .../config-extensions/webpack.config.babel.js | 4 +- resolvers/webpack/test/extensions.js | 1 - resolvers/webpack/test/fallback.js | 1 - resolvers/webpack/test/loaders.js | 1 - resolvers/webpack/test/packageMains.js | 1 - resolvers/webpack/test/root.js | 1 - scripts/testAll.js | 4 +- src/ExportMap.js | 254 +++++++++--------- src/core/importType.js | 18 +- src/core/packagePath.js | 1 - src/core/staticRequire.js | 14 +- src/index.js | 28 +- src/rules/consistent-type-specifier-style.js | 36 +-- src/rules/default.js | 10 +- src/rules/dynamic-import-chunkname.js | 3 +- src/rules/export.js | 40 +-- src/rules/exports-last.js | 6 +- src/rules/extensions.js | 18 +- src/rules/first.js | 35 +-- src/rules/group-exports.js | 10 +- src/rules/imports-first.js | 7 +- src/rules/max-dependencies.js | 32 ++- src/rules/named.js | 8 +- src/rules/namespace.js | 68 ++--- src/rules/newline-after-import.js | 35 +-- src/rules/no-absolute-path.js | 6 +- src/rules/no-amd.js | 13 +- src/rules/no-anonymous-default-export.js | 6 +- src/rules/no-commonjs.js | 46 ++-- src/rules/no-cycle.js | 44 +-- src/rules/no-default-export.js | 2 +- src/rules/no-deprecated.js | 75 +++--- src/rules/no-duplicates.js | 65 +++-- src/rules/no-dynamic-require.js | 20 +- src/rules/no-empty-named-blocks.js | 21 +- src/rules/no-extraneous-dependencies.js | 94 +++---- src/rules/no-import-module-exports.js | 21 +- src/rules/no-internal-modules.js | 34 +-- src/rules/no-mutable-exports.js | 4 +- src/rules/no-named-as-default-member.js | 105 ++++---- src/rules/no-named-as-default.js | 18 +- src/rules/no-named-default.js | 2 +- src/rules/no-named-export.js | 2 +- src/rules/no-namespace.js | 23 +- src/rules/no-nodejs-modules.js | 2 +- src/rules/no-relative-packages.js | 2 +- src/rules/no-relative-parent-imports.js | 7 +- src/rules/no-restricted-paths.js | 20 +- src/rules/no-unassigned-import.js | 37 +-- src/rules/no-unused-modules.js | 117 ++++---- src/rules/no-useless-path-segments.js | 4 +- src/rules/no-webpack-loader-syntax.js | 4 +- src/rules/order.js | 101 ++++--- src/rules/prefer-default-export.js | 24 +- tests/src/cli.js | 14 +- tests/src/core/getExports.js | 55 ++-- tests/src/core/hash.js | 2 +- tests/src/core/parse.js | 16 +- tests/src/core/resolve.js | 207 ++++++++------ tests/src/package.js | 6 +- tests/src/rules/dynamic-import-chunkname.js | 2 +- tests/src/rules/export.js | 2 - tests/src/rules/exports-last.js | 2 +- tests/src/rules/extensions.js | 25 +- tests/src/rules/first.js | 2 +- tests/src/rules/named.js | 16 +- tests/src/rules/namespace.js | 69 ++--- tests/src/rules/newline-after-import.js | 50 ++-- tests/src/rules/no-commonjs.js | 16 +- tests/src/rules/no-cycle.js | 4 +- tests/src/rules/no-deprecated.js | 13 +- tests/src/rules/no-duplicates.js | 24 +- tests/src/rules/no-dynamic-require.js | 14 +- tests/src/rules/no-empty-named-blocks.js | 3 +- tests/src/rules/no-extraneous-dependencies.js | 26 +- tests/src/rules/no-import-module-exports.js | 3 +- tests/src/rules/no-named-as-default-member.js | 25 +- tests/src/rules/no-nodejs-modules.js | 4 +- tests/src/rules/no-relative-parent-imports.js | 2 +- tests/src/rules/no-restricted-paths.js | 5 +- tests/src/rules/no-unassigned-import.js | 30 +-- tests/src/rules/no-unresolved.js | 23 +- tests/src/rules/no-unused-modules.js | 7 +- tests/src/rules/no-webpack-loader-syntax.js | 8 +- tests/src/rules/order.js | 182 ++++++------- tests/src/rules/prefer-default-export.js | 6 +- tests/src/utils.js | 12 +- utils/ModuleCache.js | 7 +- utils/declaredScope.js | 5 +- utils/hash.js | 11 +- utils/ignore.js | 7 +- utils/module-require.js | 1 + utils/moduleVisitor.js | 88 +++--- utils/parse.js | 16 +- utils/pkgUp.js | 9 +- utils/readPkgUp.js | 9 +- utils/resolve.js | 53 ++-- utils/unambiguous.js | 3 +- utils/visit.js | 1 + 106 files changed, 1405 insertions(+), 1370 deletions(-) diff --git a/.eslintrc b/.eslintrc index 1c41cb71bd..709a474484 100644 --- a/.eslintrc +++ b/.eslintrc @@ -12,12 +12,18 @@ "env": { "node": true, "es6": true, + "es2017": true, }, "parserOptions": { "sourceType": "module", "ecmaVersion": 2020, }, "rules": { + "arrow-body-style": [2, "as-needed"], + "arrow-parens": [2, "always"], + "arrow-spacing": [2, { "before": true, "after": true }], + "block-spacing": [2, "always"], + "brace-style": [2, "1tbs", { "allowSingleLine": true }], "comma-dangle": ["error", { "arrays": "always-multiline", "objects": "always-multiline", @@ -25,12 +31,47 @@ "exports": "always-multiline", "functions": "always-multiline", }], + "comma-spacing": [2, { "before": false, "after": true }], "comma-style": [2, "last"], - "curly": [2, "multi-line"], + "computed-property-spacing": [2, "never"], + "curly": [2, "all"], + "default-case": [2, { "commentPattern": "(?:)" }], + "default-case-last": [2], + "default-param-last": [2], + "dot-location": [2, "property"], + "dot-notation": [2, { "allowKeywords": true, "allowPattern": "throws" }], "eol-last": [2, "always"], "eqeqeq": [2, "allow-null"], - "func-call-spacing": 2, - "indent": [2, 2], + "for-direction": [2], + "function-call-argument-newline": [2, "consistent"], + "func-call-spacing": [2, "never"], + "implicit-arrow-linebreak": [2, "beside"], + "indent": [2, 2, { + "SwitchCase": 1, + "VariableDeclarator": 1, + "outerIIFEBody": 1, + "FunctionDeclaration": { + "parameters": 1, + "body": 1 + }, + "FunctionExpression": { + "parameters": 1, + "body": 1 + }, + "CallExpression": { + "arguments": 1 + }, + "ArrayExpression": 1, + "ObjectExpression": 1, + "ImportDeclaration": 1, + "flatTernaryExpressions": false, + }], + "jsx-quotes": [2, "prefer-double"], + "key-spacing": [2, { + "beforeColon": false, + "afterColon": true, + "mode": "strict", + }], "keyword-spacing": ["error", { "before": true, "after": true, @@ -40,27 +81,68 @@ "case": { "after": true } } }], + "linebreak-style": [2, "unix"], + "lines-around-directive": [2, { + "before": "always", + "after": "always", + }], "max-len": 0, + "new-parens": 2, + "no-array-constructor": 2, + "no-compare-neg-zero": 2, "no-cond-assign": [2, "always"], + "no-extra-parens": 2, + "no-multiple-empty-lines": [2, { "max": 1, "maxEOF": 1, "maxBOF": 0 }], "no-return-assign": [2, "always"], + "no-trailing-spaces": 2, "no-var": 2, "object-curly-spacing": [2, "always"], "object-shorthand": ["error", "always", { "ignoreConstructors": false, - "avoidQuotes": true, + "avoidQuotes": false, + "avoidExplicitReturnArrows": true, }], "one-var": [2, "never"], + "operator-linebreak": [2, "none", { + "overrides": { + "?": "before", + ":": "before", + "&&": "before", + "||": "before", + }, + }], "prefer-const": 2, + "prefer-object-spread": 2, + "prefer-rest-params": 2, + "prefer-template": 2, + "quote-props": [2, "as-needed", { "keywords": false }], "quotes": [2, "single", { "allowTemplateLiterals": true, "avoidEscape": true, }], + "rest-spread-spacing": [2, "never"], "semi": [2, "always"], + "semi-spacing": [2, { "before": false, "after": true }], + "semi-style": [2, "last"], + "space-before-blocks": [2, { "functions": "always", "keywords": "always", "classes": "always" }], "space-before-function-paren": ["error", { "anonymous": "always", "named": "never", "asyncArrow": "always", }], + "space-in-parens": [2, "never"], + "space-infix-ops": [2], + "space-unary-ops": [2, { "words": true, "nonwords": false }], + "switch-colon-spacing": [2, { "after": true, "before": false }], + "template-curly-spacing": [2, "never"], + "template-tag-spacing": [2, "never"], + "unicode-bom": [2, "never"], + "use-isnan": [2, { "enforceForSwitchCase": true }], + "valid-typeof": [2], + "wrap-iife": [2, "outside", { "functionPrototypeMethods": true }], + "wrap-regex": [2], + "yield-star-spacing": [2, { "before": false, "after": true }], + "yoda": [2, "never", { "exceptRange": true, "onlyEquality": false }], "eslint-plugin/consistent-output": [ "error", @@ -116,6 +198,9 @@ "files": "resolvers/webpack/**", "rules": { "no-console": 1, + "prefer-template": 0, + "prefer-object-spread": 0, + "prefer-rest-params": 0, }, "env": { "es6": true, @@ -143,6 +228,8 @@ "exports": "always-multiline", "functions": "never" }], + "prefer-object-spread": "off", + "prefer-template": "off", "no-console": 1, }, }, diff --git a/config/typescript.js b/config/typescript.js index ed03fb3f6c..9fd789dbf7 100644 --- a/config/typescript.js +++ b/config/typescript.js @@ -16,8 +16,8 @@ module.exports = { '@typescript-eslint/parser': ['.ts', '.tsx'], }, 'import/resolver': { - 'node': { - 'extensions': allExtensions, + node: { + extensions: allExtensions, }, }, }, diff --git a/memo-parser/index.js b/memo-parser/index.js index de558ffa3e..7868b7e953 100644 --- a/memo-parser/index.js +++ b/memo-parser/index.js @@ -17,7 +17,7 @@ const parserOptions = { }; exports.parse = function parse(content, options) { - options = Object.assign({}, options, parserOptions); + options = { ...options, ...parserOptions }; if (!options.filePath) { throw new Error('no file path provided!'); @@ -30,7 +30,7 @@ exports.parse = function parse(content, options) { const key = keyHash.digest('hex'); let ast = cache.get(key); - if (ast != null) return ast; + if (ast != null) { return ast; } const realParser = moduleRequire(options.parser); diff --git a/resolvers/node/index.js b/resolvers/node/index.js index ac478ef029..d382bca43d 100644 --- a/resolvers/node/index.js +++ b/resolvers/node/index.js @@ -29,17 +29,14 @@ exports.resolve = function (source, file, config) { }; function opts(file, config, packageFilter) { - return Object.assign({ - // more closely matches Node (#333) + return { // more closely matches Node (#333) // plus 'mjs' for native modules! (#939) extensions: ['.mjs', '.js', '.json', '.node'], - }, - config, - { + ...config, // path.resolve will handle paths relative to CWD basedir: path.dirname(path.resolve(file)), packageFilter, - }); + }; } function identity(x) { return x; } diff --git a/resolvers/node/test/packageMains.js b/resolvers/node/test/packageMains.js index caac6221ca..170b10e1a1 100644 --- a/resolvers/node/test/packageMains.js +++ b/resolvers/node/test/packageMains.js @@ -8,7 +8,6 @@ const resolver = require('../'); const file = path.join(__dirname, 'package-mains', 'dummy.js'); - describe('packageMains', function () { it('captures module', function () { expect(resolver.resolve('./module', file)).property('path') diff --git a/resolvers/node/test/paths.js b/resolvers/node/test/paths.js index 1c42b46167..e6ffdafcd9 100644 --- a/resolvers/node/test/paths.js +++ b/resolvers/node/test/paths.js @@ -11,7 +11,6 @@ describe('paths', function () { }); }); - describe('core', function () { it('returns found, but null path, for core Node modules', function () { const resolved = node.resolve('fs', './test/file.js'); @@ -20,7 +19,6 @@ describe('core', function () { }); }); - describe('default options', function () { it('finds .json files', function () { diff --git a/resolvers/webpack/index.js b/resolvers/webpack/index.js index b569d53224..8eb2db5ad6 100644 --- a/resolvers/webpack/index.js +++ b/resolvers/webpack/index.js @@ -75,7 +75,7 @@ exports.resolve = function (source, file, settings) { if (!configPath || !path.isAbsolute(configPath)) { // if not, find ancestral package.json and use its directory as base for the path packageDir = findRoot(path.resolve(file)); - if (!packageDir) throw new Error('package not found above ' + file); + if (!packageDir) { throw new Error('package not found above ' + file); } } configPath = findConfigPath(configPath, packageDir); @@ -108,7 +108,7 @@ exports.resolve = function (source, file, settings) { } if (Array.isArray(webpackConfig)) { - webpackConfig = webpackConfig.map(cfg => { + webpackConfig = webpackConfig.map((cfg) => { if (typeof cfg === 'function') { return cfg(env, argv); } @@ -284,16 +284,15 @@ function createWebpack1ResolveSync(webpackRequire, resolveConfig, plugins) { new ResultSymlinkPlugin(), ); - const resolvePlugins = []; // support webpack.ResolverPlugin if (plugins) { plugins.forEach(function (plugin) { if ( - plugin.constructor && - plugin.constructor.name === 'ResolverPlugin' && - Array.isArray(plugin.plugins) + plugin.constructor + && plugin.constructor.name === 'ResolverPlugin' + && Array.isArray(plugin.plugins) ) { resolvePlugins.push.apply(resolvePlugins, plugin.plugins); } @@ -324,10 +323,10 @@ function makeRootPlugin(ModulesInRootPlugin, name, root) { /* eslint-enable */ function findExternal(source, externals, context, resolveSync) { - if (!externals) return false; + if (!externals) { return false; } // string match - if (typeof externals === 'string') return (source === externals); + if (typeof externals === 'string') { return source === externals; } // array: recurse if (Array.isArray(externals)) { @@ -384,8 +383,8 @@ function findExternal(source, externals, context, resolveSync) { // else, vanilla object for (const key in externals) { - if (!has(externals, key)) continue; - if (source === key) return true; + if (!has(externals, key)) { continue; } + if (source === key) { return true; } } return false; } @@ -396,7 +395,6 @@ function findConfigPath(configPath, packageDir) { }); let extension; - if (configPath) { // extensions is not reused below, so safe to mutate it here. extensions.reverse(); diff --git a/resolvers/webpack/test/config-extensions/webpack.config.babel.js b/resolvers/webpack/test/config-extensions/webpack.config.babel.js index a63434f9bb..c8b3cd5780 100644 --- a/resolvers/webpack/test/config-extensions/webpack.config.babel.js +++ b/resolvers/webpack/test/config-extensions/webpack.config.babel.js @@ -3,7 +3,7 @@ import path from 'path'; export default { resolve: { alias: { - 'foo': path.join(__dirname, 'some', 'goofy', 'path', 'foo.js'), + foo: path.join(__dirname, 'some', 'goofy', 'path', 'foo.js'), }, modules: [ path.join(__dirname, 'src'), @@ -17,7 +17,7 @@ export default { }, externals: [ - { 'jquery': 'jQuery' }, + { jquery: 'jQuery' }, 'bootstrap', ], }; diff --git a/resolvers/webpack/test/extensions.js b/resolvers/webpack/test/extensions.js index c028f5c913..096df77281 100644 --- a/resolvers/webpack/test/extensions.js +++ b/resolvers/webpack/test/extensions.js @@ -6,7 +6,6 @@ const path = require('path'); const resolve = require('../index').resolve; - const file = path.join(__dirname, 'files', 'dummy.js'); const extensions = path.join(__dirname, 'custom-extensions', 'dummy.js'); diff --git a/resolvers/webpack/test/fallback.js b/resolvers/webpack/test/fallback.js index 87c15eecd7..b164209e14 100644 --- a/resolvers/webpack/test/fallback.js +++ b/resolvers/webpack/test/fallback.js @@ -6,7 +6,6 @@ const path = require('path'); const resolve = require('../index').resolve; - const file = path.join(__dirname, 'files', 'src', 'dummy.js'); describe('fallback', function () { diff --git a/resolvers/webpack/test/loaders.js b/resolvers/webpack/test/loaders.js index 6b5604592d..e250894a54 100644 --- a/resolvers/webpack/test/loaders.js +++ b/resolvers/webpack/test/loaders.js @@ -6,7 +6,6 @@ const path = require('path'); const resolve = require('../index').resolve; - const file = path.join(__dirname, 'files', 'dummy.js'); describe('inline loader syntax', function () { diff --git a/resolvers/webpack/test/packageMains.js b/resolvers/webpack/test/packageMains.js index fef3dde073..d3ddad9dab 100644 --- a/resolvers/webpack/test/packageMains.js +++ b/resolvers/webpack/test/packageMains.js @@ -8,7 +8,6 @@ const resolver = require('../'); const file = path.join(__dirname, 'package-mains', 'dummy.js'); - describe('packageMains', function () { it('captures module', function () { diff --git a/resolvers/webpack/test/root.js b/resolvers/webpack/test/root.js index 154dbeef95..194bb8fc88 100644 --- a/resolvers/webpack/test/root.js +++ b/resolvers/webpack/test/root.js @@ -6,7 +6,6 @@ const path = require('path'); const resolve = require('../index').resolve; - const file = path.join(__dirname, 'files', 'src', 'dummy.js'); const webpackDir = path.join(__dirname, 'different-package-location'); diff --git a/scripts/testAll.js b/scripts/testAll.js index fc30b1ac7d..0e4a12c68a 100644 --- a/scripts/testAll.js +++ b/scripts/testAll.js @@ -10,11 +10,11 @@ const spawnOptions = { spawnSync( npmPath, ['test'], - Object.assign({ cwd: __dirname }, spawnOptions)); + { cwd: __dirname, ...spawnOptions }); for (const resolverDir of resolverDirectories) { spawnSync( npmPath, ['test'], - Object.assign({ cwd: resolverDir }, spawnOptions)); + { cwd: resolverDir, ...spawnOptions }); } diff --git a/src/ExportMap.js b/src/ExportMap.js index d31375c83d..cd5bad56c3 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -53,10 +53,10 @@ export default class ExportMap { get size() { let size = this.namespace.size + this.reexports.size; - this.dependencies.forEach(dep => { + this.dependencies.forEach((dep) => { const d = dep(); // CJS / ignored dependencies won't exist (#717) - if (d == null) return; + if (d == null) { return; } size += d.size; }); return size; @@ -70,8 +70,8 @@ export default class ExportMap { * @return {Boolean} true if `name` is exported by this module. */ has(name) { - if (this.namespace.has(name)) return true; - if (this.reexports.has(name)) return true; + if (this.namespace.has(name)) { return true; } + if (this.reexports.has(name)) { return true; } // default exports must be explicitly re-exported (#328) if (name !== 'default') { @@ -79,9 +79,9 @@ export default class ExportMap { const innerMap = dep(); // todo: report as unresolved? - if (!innerMap) continue; + if (!innerMap) { continue; } - if (innerMap.has(name)) return true; + if (innerMap.has(name)) { return true; } } } @@ -94,14 +94,14 @@ export default class ExportMap { * @return {{ found: boolean, path: ExportMap[] }} */ hasDeep(name) { - if (this.namespace.has(name)) return { found: true, path: [this] }; + if (this.namespace.has(name)) { return { found: true, path: [this] }; } if (this.reexports.has(name)) { const reexports = this.reexports.get(name); const imported = reexports.getImport(); // if import is ignored, return explicit 'null' - if (imported == null) return { found: true, path: [this] }; + if (imported == null) { return { found: true, path: [this] }; } // safeguard against cycles, only if name matches if (imported.path === this.path && reexports.local === name) { @@ -114,17 +114,16 @@ export default class ExportMap { return deep; } - // default exports must be explicitly re-exported (#328) if (name !== 'default') { for (const dep of this.dependencies) { const innerMap = dep(); - if (innerMap == null) return { found: true, path: [this] }; + if (innerMap == null) { return { found: true, path: [this] }; } // todo: report as unresolved? - if (!innerMap) continue; + if (!innerMap) { continue; } // safeguard against cycles - if (innerMap.path === this.path) continue; + if (innerMap.path === this.path) { continue; } const innerValue = innerMap.hasDeep(name); if (innerValue.found) { @@ -138,17 +137,17 @@ export default class ExportMap { } get(name) { - if (this.namespace.has(name)) return this.namespace.get(name); + if (this.namespace.has(name)) { return this.namespace.get(name); } if (this.reexports.has(name)) { const reexports = this.reexports.get(name); const imported = reexports.getImport(); // if import is ignored, return explicit 'null' - if (imported == null) return null; + if (imported == null) { return null; } // safeguard against cycles, only if name matches - if (imported.path === this.path && reexports.local === name) return undefined; + if (imported.path === this.path && reexports.local === name) { return undefined; } return imported.get(reexports.local); } @@ -158,13 +157,13 @@ export default class ExportMap { for (const dep of this.dependencies) { const innerMap = dep(); // todo: report as unresolved? - if (!innerMap) continue; + if (!innerMap) { continue; } // safeguard against cycles - if (innerMap.path === this.path) continue; + if (innerMap.path === this.path) { continue; } const innerValue = innerMap.get(name); - if (innerValue !== undefined) return innerValue; + if (innerValue !== undefined) { return innerValue; } } } @@ -172,8 +171,7 @@ export default class ExportMap { } forEach(callback, thisArg) { - this.namespace.forEach((v, n) => - callback.call(thisArg, v, n, this)); + this.namespace.forEach((v, n) => { callback.call(thisArg, v, n, this); }); this.reexports.forEach((reexports, name) => { const reexported = reexports.getImport(); @@ -181,25 +179,28 @@ export default class ExportMap { callback.call(thisArg, reexported && reexported.get(reexports.local), name, this); }); - this.dependencies.forEach(dep => { + this.dependencies.forEach((dep) => { const d = dep(); // CJS / ignored dependencies won't exist (#717) - if (d == null) return; + if (d == null) { return; } - d.forEach((v, n) => - n !== 'default' && callback.call(thisArg, v, n, this)); + d.forEach((v, n) => { + if (n !== 'default') { + callback.call(thisArg, v, n, this); + } + }); }); } // todo: keys, values, entries? reportErrors(context, declaration) { + const msg = this.errors + .map((e) => `${e.message} (${e.lineNumber}:${e.column})`) + .join(', '); context.report({ node: declaration.source, - message: `Parse errors in imported module '${declaration.source.value}': ` + - `${this.errors - .map(e => `${e.message} (${e.lineNumber}:${e.column})`) - .join(', ')}`, + message: `Parse errors in imported module '${declaration.source.value}': ${msg}`, }); } } @@ -211,7 +212,7 @@ function captureDoc(source, docStyleParsers, ...nodes) { const metadata = {}; // 'some' short-circuits on first 'true' - nodes.some(n => { + nodes.some((n) => { try { let leadingComments; @@ -223,7 +224,7 @@ function captureDoc(source, docStyleParsers, ...nodes) { leadingComments = source.getCommentsBefore(n); } - if (!leadingComments || leadingComments.length === 0) return false; + if (!leadingComments || leadingComments.length === 0) { return false; } for (const name in docStyleParsers) { const doc = docStyleParsers[name](leadingComments); @@ -255,9 +256,9 @@ function captureJsDoc(comments) { let doc; // capture XSDoc - comments.forEach(comment => { + comments.forEach((comment) => { // skip non-block comments - if (comment.type !== 'Block') return; + if (comment.type !== 'Block') { return; } try { doc = doctrine.parse(comment.value, { unwrap: true }); } catch (err) { @@ -276,7 +277,7 @@ function captureTomDoc(comments) { const lines = []; for (let i = 0; i < comments.length; i++) { const comment = comments[i]; - if (comment.value.match(/^\s*$/)) break; + if (comment.value.match(/^\s*$/)) { break; } lines.push(comment.value.trim()); } @@ -297,7 +298,7 @@ const supportedImportTypes = new Set(['ImportDefaultSpecifier', 'ImportNamespace ExportMap.get = function (source, context) { const path = resolve(source, context); - if (path == null) return null; + if (path == null) { return null; } return ExportMap.for(childContext(path, context)); }; @@ -309,7 +310,7 @@ ExportMap.for = function (context) { let exportMap = exportCache.get(cacheKey); // return cached ignore - if (exportMap === null) return null; + if (exportMap === null) { return null; } const stats = fs.statSync(path); if (exportMap != null) { @@ -358,7 +359,6 @@ ExportMap.for = function (context) { return exportMap; }; - ExportMap.parse = function (path, content, context) { const m = new ExportMap(path); const isEsModuleInteropTrue = isEsModuleInterop(); @@ -416,21 +416,21 @@ ExportMap.parse = function (path, content, context) { }); const unambiguouslyESM = unambiguous.isModule(ast); - if (!unambiguouslyESM && !hasDynamicImports) return null; + if (!unambiguouslyESM && !hasDynamicImports) { return null; } - const docstyle = (context.settings && context.settings['import/docstyle']) || ['jsdoc']; + const docstyle = context.settings && context.settings['import/docstyle'] || ['jsdoc']; const docStyleParsers = {}; - docstyle.forEach(style => { + docstyle.forEach((style) => { docStyleParsers[style] = availableDocStyleParsers[style]; }); // attempt to collect module doc if (ast.comments) { - ast.comments.some(c => { - if (c.type !== 'Block') return false; + ast.comments.some((c) => { + if (c.type !== 'Block') { return false; } try { const doc = doctrine.parse(c.value, { unwrap: true }); - if (doc.tags.some(t => t.title === 'module')) { + if (doc.tags.some((t) => t.title === 'module')) { m.doc = doc; return true; } @@ -447,12 +447,12 @@ ExportMap.parse = function (path, content, context) { function resolveImport(value) { const rp = remotePath(value); - if (rp == null) return null; + if (rp == null) { return null; } return ExportMap.for(childContext(rp, context)); } function getNamespace(identifier) { - if (!namespaces.has(identifier.name)) return; + if (!namespaces.has(identifier.name)) { return; } return function () { return resolveImport(namespaces.get(identifier.name)); @@ -474,27 +474,27 @@ ExportMap.parse = function (path, content, context) { let local; switch (s.type) { - case 'ExportDefaultSpecifier': - if (!nsource) return; - local = 'default'; - break; - case 'ExportNamespaceSpecifier': - m.namespace.set(s.exported.name, Object.defineProperty(exportMeta, 'namespace', { - get() { return resolveImport(nsource); }, - })); - return; - case 'ExportAllDeclaration': - 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 || s.exported.value, addNamespace(exportMeta, s.local)); + case 'ExportDefaultSpecifier': + if (!nsource) { return; } + local = 'default'; + break; + case 'ExportNamespaceSpecifier': + m.namespace.set(s.exported.name, Object.defineProperty(exportMeta, 'namespace', { + get() { return resolveImport(nsource); }, + })); return; - } + case 'ExportAllDeclaration': + 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 || s.exported.value, addNamespace(exportMeta, s.local)); + return; + } // else falls through - default: - local = s.local.name; - break; + default: + local = s.local.name; + break; } // todo: JSDoc @@ -508,7 +508,7 @@ ExportMap.parse = function (path, content, context) { // shouldn't be considered to be just importing types let specifiersOnlyImportingTypes = n.specifiers.length > 0; const importedSpecifiers = new Set(); - n.specifiers.forEach(specifier => { + n.specifiers.forEach((specifier) => { if (specifier.type === 'ImportSpecifier') { importedSpecifiers.add(specifier.imported.name || specifier.imported.value); } else if (supportedImportTypes.has(specifier.type)) { @@ -523,10 +523,10 @@ ExportMap.parse = function (path, content, context) { } function captureDependency({ source }, isOnlyImportingTypes, importedSpecifiers = new Set()) { - if (source == null) return null; + if (source == null) { return null; } const p = remotePath(source.value); - if (p == null) return null; + if (p == null) { return null; } const declarationMetadata = { // capturing actual node reference holds full AST in memory! @@ -550,9 +550,7 @@ ExportMap.parse = function (path, content, context) { function readTsConfig(context) { const tsConfigInfo = tsConfigLoader({ - cwd: - (context.parserOptions && context.parserOptions.tsconfigRootDir) || - process.cwd(), + cwd: context.parserOptions && context.parserOptions.tsconfigRootDir || process.cwd(), getEnv: (key) => process.env[key], }); try { @@ -599,7 +597,7 @@ ExportMap.parse = function (path, content, context) { if (n.type === 'ExportAllDeclaration') { const getter = captureDependency(n, n.exportKind === 'type'); - if (getter) m.dependencies.add(getter); + if (getter) { m.dependencies.add(getter); } if (n.exported) { processSpecifier(n, n.exported, m); } @@ -610,7 +608,7 @@ ExportMap.parse = function (path, content, context) { if (n.type === 'ImportDeclaration') { captureDependencyWithSpecifiers(n); - const ns = n.specifiers.find(s => s.type === 'ImportNamespaceSpecifier'); + const ns = n.specifiers.find((s) => s.type === 'ImportNamespaceSpecifier'); if (ns) { namespaces.set(ns.local.name, n.source.value); } @@ -623,24 +621,28 @@ ExportMap.parse = function (path, content, context) { // capture declaration if (n.declaration != null) { switch (n.declaration.type) { - case 'FunctionDeclaration': - case 'ClassDeclaration': - case 'TypeAlias': // flowtype with babel-eslint parser - case 'InterfaceDeclaration': - case 'DeclareFunction': - case 'TSDeclareFunction': - case 'TSEnumDeclaration': - case 'TSTypeAliasDeclaration': - case 'TSInterfaceDeclaration': - case 'TSAbstractClassDeclaration': - case 'TSModuleDeclaration': - m.namespace.set(n.declaration.id.name, captureDoc(source, docStyleParsers, n)); - break; - case 'VariableDeclaration': - n.declaration.declarations.forEach((d) => - recursivePatternCapture(d.id, - id => m.namespace.set(id.name, captureDoc(source, docStyleParsers, d, n)))); - break; + case 'FunctionDeclaration': + case 'ClassDeclaration': + case 'TypeAlias': // flowtype with babel-eslint parser + case 'InterfaceDeclaration': + case 'DeclareFunction': + case 'TSDeclareFunction': + case 'TSEnumDeclaration': + case 'TSTypeAliasDeclaration': + case 'TSInterfaceDeclaration': + case 'TSAbstractClassDeclaration': + case 'TSModuleDeclaration': + m.namespace.set(n.declaration.id.name, captureDoc(source, docStyleParsers, n)); + break; + case 'VariableDeclaration': + n.declaration.declarations.forEach((d) => { + recursivePatternCapture( + d.id, + (id) => m.namespace.set(id.name, captureDoc(source, docStyleParsers, d, n)), + ); + }); + break; + default: } } @@ -656,7 +658,7 @@ ExportMap.parse = function (path, content, context) { if (includes(exports, n.type)) { const exportedName = n.type === 'TSNamespaceExportDeclaration' ? (n.id || n.name).name - : (n.expression && n.expression.name || (n.expression.id && n.expression.id.name) || null); + : n.expression && n.expression.name || n.expression.id && n.expression.id.name || null; const declTypes = [ 'VariableDeclaration', 'ClassDeclaration', @@ -668,7 +670,7 @@ ExportMap.parse = function (path, content, context) { 'TSModuleDeclaration', ]; const exportedDecls = ast.body.filter(({ type, id, declarations }) => includes(declTypes, type) && ( - (id && id.name === exportedName) || (declarations && declarations.find((d) => d.id.name === exportedName)) + id && id.name === exportedName || declarations && declarations.find((d) => d.id.name === exportedName) )); if (exportedDecls.length === 0) { // Export is not referencing any local declaration, must be re-exporting @@ -689,18 +691,17 @@ ExportMap.parse = function (path, content, context) { decl.body.body.forEach((moduleBlockNode) => { // Export-assignment exports all members in the namespace, // explicitly exported or not. - const namespaceDecl = moduleBlockNode.type === 'ExportNamedDeclaration' ? - moduleBlockNode.declaration : - moduleBlockNode; + const namespaceDecl = moduleBlockNode.type === 'ExportNamedDeclaration' + ? moduleBlockNode.declaration + : moduleBlockNode; if (!namespaceDecl) { // TypeScript can check this for us; we needn't } else if (namespaceDecl.type === 'VariableDeclaration') { - namespaceDecl.declarations.forEach((d) => - recursivePatternCapture(d.id, (id) => m.namespace.set( - id.name, - captureDoc(source, docStyleParsers, decl, namespaceDecl, moduleBlockNode), - )), + namespaceDecl.declarations.forEach((d) => recursivePatternCapture(d.id, (id) => m.namespace.set( + id.name, + captureDoc(source, docStyleParsers, decl, namespaceDecl, moduleBlockNode), + )), ); } else { m.namespace.set( @@ -740,7 +741,6 @@ function thunkFor(p, context) { return () => ExportMap.for(childContext(p, context)); } - /** * Traverse a pattern/identifier node, calling 'callback' * for each leaf identifier. @@ -750,34 +750,35 @@ function thunkFor(p, context) { */ export function recursivePatternCapture(pattern, callback) { switch (pattern.type) { - case 'Identifier': // base case - callback(pattern); - break; - - case 'ObjectPattern': - pattern.properties.forEach(p => { - if (p.type === 'ExperimentalRestProperty' || p.type === 'RestElement') { - callback(p.argument); - return; - } - recursivePatternCapture(p.value, callback); - }); - break; + case 'Identifier': // base case + callback(pattern); + break; - case 'ArrayPattern': - pattern.elements.forEach((element) => { - if (element == null) return; - if (element.type === 'ExperimentalRestProperty' || element.type === 'RestElement') { - callback(element.argument); - return; - } - recursivePatternCapture(element, callback); - }); - break; + case 'ObjectPattern': + pattern.properties.forEach((p) => { + if (p.type === 'ExperimentalRestProperty' || p.type === 'RestElement') { + callback(p.argument); + return; + } + recursivePatternCapture(p.value, callback); + }); + break; - case 'AssignmentPattern': - callback(pattern.left); - break; + case 'ArrayPattern': + pattern.elements.forEach((element) => { + if (element == null) { return; } + if (element.type === 'ExperimentalRestProperty' || element.type === 'RestElement') { + callback(element.argument); + return; + } + recursivePatternCapture(element, callback); + }); + break; + + case 'AssignmentPattern': + callback(pattern.left); + break; + default: } } @@ -811,7 +812,6 @@ function childContext(path, context) { }; } - /** * sometimes legacy support isn't _that_ hard... right? */ diff --git a/src/core/importType.js b/src/core/importType.js index ebdb306bc9..6a37d1bb14 100644 --- a/src/core/importType.js +++ b/src/core/importType.js @@ -14,7 +14,7 @@ function baseModule(name) { } function isInternalRegexMatch(name, settings) { - const internalScope = (settings && settings['import/internal-regex']); + const internalScope = settings && settings['import/internal-regex']; return internalScope && new RegExp(internalScope).test(name); } @@ -24,21 +24,21 @@ export function isAbsolute(name) { // path is defined only when a resolver resolves to a non-standard path export function isBuiltIn(name, settings, path) { - if (path || !name) return false; + if (path || !name) { return false; } const base = baseModule(name); - const extras = (settings && settings['import/core-modules']) || []; + const extras = settings && settings['import/core-modules'] || []; return isCoreModule(base) || extras.indexOf(base) > -1; } export function isExternalModule(name, path, context) { - if (arguments.length < 3) { + if (arguments.length < 3) { throw new TypeError('isExternalModule: name, path, and context are all required'); } return (isModule(name) || isScoped(name)) && typeTest(name, context, path) === 'external'; } export function isExternalModuleMain(name, path, context) { - if (arguments.length < 3) { + if (arguments.length < 3) { throw new TypeError('isExternalModule: name, path, and context are all required'); } return isModuleMain(name) && typeTest(name, context, path) === 'external'; @@ -65,7 +65,7 @@ export function isScopedMain(name) { } function isRelativeToParent(name) { - return /^\.\.$|^\.\.[\\/]/.test(name); + return (/^\.\.$|^\.\.[\\/]/).test(name); } const indexFiles = ['.', './', './index', './index.js']; @@ -74,7 +74,7 @@ function isIndex(name) { } function isRelativeToSibling(name) { - return /^\.[\\/]/.test(name); + return (/^\.[\\/]/).test(name); } function isExternalPath(path, context) { @@ -89,7 +89,7 @@ function isExternalPath(path, context) { return true; } - const folders = (settings && settings['import/external-module-folders']) || ['node_modules']; + const folders = settings && settings['import/external-module-folders'] || ['node_modules']; return folders.some((folder) => { const folderPath = nodeResolve(packagePath, folder); const relativePath = relative(folderPath, path); @@ -109,7 +109,7 @@ function isExternalLookingName(name) { return isModule(name) || isScoped(name); } -function typeTest(name, context, path ) { +function typeTest(name, context, path) { const { settings } = context; if (isInternalRegexMatch(name, settings)) { return 'internal'; } if (isAbsolute(name, settings, path)) { return 'absolute'; } diff --git a/src/core/packagePath.js b/src/core/packagePath.js index 2b5a2d41ef..1a7a28f4b4 100644 --- a/src/core/packagePath.js +++ b/src/core/packagePath.js @@ -2,7 +2,6 @@ import { dirname } from 'path'; import pkgUp from 'eslint-module-utils/pkgUp'; import readPkgUp from 'eslint-module-utils/readPkgUp'; - export function getContextPackagePath(context) { return getFilePackagePath(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()); } diff --git a/src/core/staticRequire.js b/src/core/staticRequire.js index 502d39317d..88b5000c89 100644 --- a/src/core/staticRequire.js +++ b/src/core/staticRequire.js @@ -1,10 +1,10 @@ // todo: merge with module visitor export default function isStaticRequire(node) { - return node && - node.callee && - node.callee.type === 'Identifier' && - node.callee.name === 'require' && - node.arguments.length === 1 && - node.arguments[0].type === 'Literal' && - typeof node.arguments[0].value === 'string'; + return node + && node.callee + && node.callee.type === 'Identifier' + && node.callee.name === 'require' + && node.arguments.length === 1 + && node.arguments[0].type === 'Literal' + && typeof node.arguments[0].value === 'string'; } diff --git a/src/index.js b/src/index.js index 15f98d96f2..feafba9003 100644 --- a/src/index.js +++ b/src/index.js @@ -1,12 +1,12 @@ export const rules = { 'no-unresolved': require('./rules/no-unresolved'), - 'named': require('./rules/named'), - 'default': require('./rules/default'), - 'namespace': require('./rules/namespace'), + named: require('./rules/named'), + default: require('./rules/default'), + namespace: require('./rules/namespace'), 'no-namespace': require('./rules/no-namespace'), - 'export': require('./rules/export'), + export: require('./rules/export'), 'no-mutable-exports': require('./rules/no-mutable-exports'), - 'extensions': require('./rules/extensions'), + extensions: require('./rules/extensions'), 'no-restricted-paths': require('./rules/no-restricted-paths'), 'no-internal-modules': require('./rules/no-internal-modules'), 'group-exports': require('./rules/group-exports'), @@ -25,19 +25,19 @@ export const rules = { 'no-commonjs': require('./rules/no-commonjs'), 'no-amd': require('./rules/no-amd'), 'no-duplicates': require('./rules/no-duplicates'), - 'first': require('./rules/first'), + first: require('./rules/first'), 'max-dependencies': require('./rules/max-dependencies'), 'no-extraneous-dependencies': require('./rules/no-extraneous-dependencies'), 'no-absolute-path': require('./rules/no-absolute-path'), 'no-nodejs-modules': require('./rules/no-nodejs-modules'), 'no-webpack-loader-syntax': require('./rules/no-webpack-loader-syntax'), - 'order': require('./rules/order'), + order: require('./rules/order'), 'newline-after-import': require('./rules/newline-after-import'), 'prefer-default-export': require('./rules/prefer-default-export'), 'no-default-export': require('./rules/no-default-export'), 'no-named-export': require('./rules/no-named-export'), 'no-dynamic-require': require('./rules/no-dynamic-require'), - 'unambiguous': require('./rules/unambiguous'), + unambiguous: require('./rules/unambiguous'), 'no-unassigned-import': require('./rules/no-unassigned-import'), 'no-useless-path-segments': require('./rules/no-useless-path-segments'), 'dynamic-import-chunkname': require('./rules/dynamic-import-chunkname'), @@ -55,17 +55,17 @@ export const rules = { }; export const configs = { - 'recommended': require('../config/recommended'), + recommended: require('../config/recommended'), - 'errors': require('../config/errors'), - 'warnings': require('../config/warnings'), + errors: require('../config/errors'), + warnings: require('../config/warnings'), // shhhh... work in progress "secret" rules 'stage-0': require('../config/stage-0'), // useful stuff for folks using various environments - 'react': require('../config/react'), + react: require('../config/react'), 'react-native': require('../config/react-native'), - 'electron': require('../config/electron'), - 'typescript': require('../config/typescript'), + electron: require('../config/electron'), + typescript: require('../config/typescript'), }; diff --git a/src/rules/consistent-type-specifier-style.js b/src/rules/consistent-type-specifier-style.js index bb8fdf8498..9119976b19 100644 --- a/src/rules/consistent-type-specifier-style.js +++ b/src/rules/consistent-type-specifier-style.js @@ -26,7 +26,7 @@ function getImportText( return ''; } - const names = specifiers.map(s => { + const names = specifiers.map((s) => { if (s.imported.name === s.local.name) { return s.imported.name; } @@ -67,12 +67,14 @@ module.exports = { if ( // no specifiers (import type {} from '') have no specifiers to mark as inline - node.specifiers.length === 0 || - (node.specifiers.length === 1 && - // default imports are both "inline" and "top-level" - (node.specifiers[0].type === 'ImportDefaultSpecifier' || - // namespace imports are both "inline" and "top-level" - node.specifiers[0].type === 'ImportNamespaceSpecifier')) + node.specifiers.length === 0 + || node.specifiers.length === 1 + // default imports are both "inline" and "top-level" + && ( + node.specifiers[0].type === 'ImportDefaultSpecifier' + // namespace imports are both "inline" and "top-level" + || node.specifiers[0].type === 'ImportNamespaceSpecifier' + ) ) { return; } @@ -101,15 +103,17 @@ module.exports = { ImportDeclaration(node) { if ( // already top-level is valid - node.importKind === 'type' || - node.importKind === 'typeof' || + node.importKind === 'type' + || node.importKind === 'typeof' // no specifiers (import {} from '') cannot have inline - so is valid - node.specifiers.length === 0 || - (node.specifiers.length === 1 && - // default imports are both "inline" and "top-level" - (node.specifiers[0].type === 'ImportDefaultSpecifier' || - // namespace imports are both "inline" and "top-level" - node.specifiers[0].type === 'ImportNamespaceSpecifier')) + || node.specifiers.length === 0 + || node.specifiers.length === 1 + // default imports are both "inline" and "top-level" + && ( + node.specifiers[0].type === 'ImportDefaultSpecifier' + // namespace imports are both "inline" and "top-level" + || node.specifiers[0].type === 'ImportNamespaceSpecifier' + ) ) { return; } @@ -195,7 +199,7 @@ module.exports = { const comma = sourceCode.getTokenAfter(defaultSpecifier, isComma); const closingBrace = sourceCode.getTokenAfter( node.specifiers[node.specifiers.length - 1], - token => token.type === 'Punctuator' && token.value === '}', + (token) => token.type === 'Punctuator' && token.value === '}', ); fixes.push(fixer.removeRange([ comma.range[0], diff --git a/src/rules/default.js b/src/rules/default.js index 6ca918ef66..f6b786020d 100644 --- a/src/rules/default.js +++ b/src/rules/default.js @@ -17,12 +17,12 @@ module.exports = { function checkDefault(specifierType, node) { const defaultSpecifier = node.specifiers.find( - specifier => specifier.type === specifierType, + (specifier) => specifier.type === specifierType, ); - if (!defaultSpecifier) return; + if (!defaultSpecifier) { return; } const imports = Exports.get(node.source.value, context); - if (imports == null) return; + if (imports == null) { return; } if (imports.errors.length) { imports.reportErrors(context, node); @@ -35,8 +35,8 @@ module.exports = { } return { - 'ImportDeclaration': checkDefault.bind(null, 'ImportDefaultSpecifier'), - 'ExportNamedDeclaration': checkDefault.bind(null, 'ExportDefaultSpecifier'), + ImportDeclaration: checkDefault.bind(null, 'ImportDefaultSpecifier'), + ExportNamedDeclaration: checkDefault.bind(null, 'ExportDefaultSpecifier'), }; }, }; diff --git a/src/rules/dynamic-import-chunkname.js b/src/rules/dynamic-import-chunkname.js index 87a8523dad..96ceff2e16 100644 --- a/src/rules/dynamic-import-chunkname.js +++ b/src/rules/dynamic-import-chunkname.js @@ -72,8 +72,7 @@ module.exports = { try { // just like webpack itself does vm.runInNewContext(`(function() {return {${comment.value}}})()`); - } - catch (error) { + } catch (error) { context.report({ node, message: `dynamic imports require a "webpack" comment with valid syntax`, diff --git a/src/rules/export.js b/src/rules/export.js index 92583bdd8f..c540f1e3c9 100644 --- a/src/rules/export.js +++ b/src/rules/export.js @@ -38,19 +38,20 @@ const tsTypePrefix = 'type:'; function isTypescriptFunctionOverloads(nodes) { const nodesArr = Array.from(nodes); - const idents = flatMap(nodesArr, (node) => ( - node.declaration && ( + const idents = flatMap( + nodesArr, + (node) => node.declaration && ( node.declaration.type === 'TSDeclareFunction' // eslint 6+ || node.declaration.type === 'TSEmptyBodyFunctionDeclaration' // eslint 4-5 ) ? node.declaration.id.name - : [] - )); + : [], + ); if (new Set(idents).size !== idents.length) { return true; } - const types = new Set(nodesArr.map(node => node.parent.type)); + const types = new Set(nodesArr.map((node) => node.parent.type)); if (!types.has('TSDeclareFunction')) { return false; } @@ -73,17 +74,17 @@ function isTypescriptFunctionOverloads(nodes) { * @returns {boolean} */ function isTypescriptNamespaceMerging(nodes) { - const types = new Set(Array.from(nodes, node => node.parent.type)); + 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')) + || 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) + || types.size === 2 && (types.has('ClassDeclaration') || types.has('TSEnumDeclaration')) && noNamespaceNodes.length === 1 ); } @@ -99,7 +100,7 @@ function isTypescriptNamespaceMerging(nodes) { * @returns {boolean} */ function shouldSkipTypescriptNamespace(node, nodes) { - const types = new Set(Array.from(nodes, node => node.parent.type)); + const types = new Set(Array.from(nodes, (node) => node.parent.type)); return !isTypescriptNamespaceMerging(nodes) && node.parent.type === 'TSModuleDeclaration' @@ -166,7 +167,7 @@ module.exports = { }, ExportNamedDeclaration(node) { - if (node.declaration == null) return; + if (node.declaration == null) { return; } const parent = getParent(node); // support for old TypeScript versions @@ -185,20 +186,19 @@ module.exports = { if (node.declaration.declarations != null) { for (const declaration of node.declaration.declarations) { - recursivePatternCapture(declaration.id, v => - addNamed(v.name, v, parent, isTypeVariableDecl)); + recursivePatternCapture(declaration.id, (v) => { addNamed(v.name, v, parent, isTypeVariableDecl); }); } } }, ExportAllDeclaration(node) { - if (node.source == null) return; // not sure if this is ever true + if (node.source == null) { return; } // not sure if this is ever true // `export * as X from 'path'` does not conflict - if (node.exported && node.exported.name) return; + if (node.exported && node.exported.name) { return; } const remoteExports = ExportMap.get(node.source.value, context); - if (remoteExports == null) return; + if (remoteExports == null) { return; } if (remoteExports.errors.length) { remoteExports.reportErrors(context, node); @@ -223,15 +223,15 @@ module.exports = { } }, - 'Program:exit': function () { + 'Program:exit'() { for (const [, named] of namespace) { for (const [name, nodes] of named) { - if (nodes.size <= 1) continue; + if (nodes.size <= 1) { continue; } - if (isTypescriptFunctionOverloads(nodes) || isTypescriptNamespaceMerging(nodes)) continue; + if (isTypescriptFunctionOverloads(nodes) || isTypescriptNamespaceMerging(nodes)) { continue; } for (const node of nodes) { - if (shouldSkipTypescriptNamespace(node, nodes)) continue; + if (shouldSkipTypescriptNamespace(node, nodes)) { continue; } if (name === 'default') { context.report(node, 'Multiple default exports.'); diff --git a/src/rules/exports-last.js b/src/rules/exports-last.js index ed77758d20..c4ed97e22f 100644 --- a/src/rules/exports-last.js +++ b/src/rules/exports-last.js @@ -1,9 +1,9 @@ import docsUrl from '../docsUrl'; function isNonExportStatement({ type }) { - return type !== 'ExportDefaultDeclaration' && - type !== 'ExportNamedDeclaration' && - type !== 'ExportAllDeclaration'; + return type !== 'ExportDefaultDeclaration' + && type !== 'ExportNamedDeclaration' + && type !== 'ExportAllDeclaration'; } module.exports = { diff --git a/src/rules/extensions.js b/src/rules/extensions.js index 7d026c787f..50debc6c8c 100644 --- a/src/rules/extensions.js +++ b/src/rules/extensions.js @@ -13,8 +13,8 @@ const patternProperties = { const properties = { type: 'object', properties: { - 'pattern': patternProperties, - 'ignorePackages': { type: 'boolean' }, + pattern: patternProperties, + ignorePackages: { type: 'boolean' }, }, }; @@ -26,7 +26,7 @@ function buildProperties(context) { ignorePackages: false, }; - context.options.forEach(obj => { + context.options.forEach((obj) => { // If this is a string, set defaultConfig to its value if (typeof obj === 'string') { @@ -132,25 +132,25 @@ module.exports = { function isExternalRootModule(file) { const slashCount = file.split('/').length - 1; - if (slashCount === 0) return true; - if (isScoped(file) && slashCount <= 1) return true; + if (slashCount === 0) { return true; } + if (isScoped(file) && slashCount <= 1) { return true; } return false; } function checkFileExtension(source, node) { // bail if the declaration doesn't have a source, e.g. "export { foo };", or if it's only partially typed like in an editor - if (!source || !source.value) return; + if (!source || !source.value) { return; } const importPathWithQueryString = source.value; // don't enforce anything on builtins - if (isBuiltIn(importPathWithQueryString, context.settings)) return; + if (isBuiltIn(importPathWithQueryString, context.settings)) { return; } const importPath = importPathWithQueryString.replace(/\?(.*)$/, ''); // don't enforce in root external packages as they may have names with `.js`. // Like `import Decimal from decimal.js`) - if (isExternalRootModule(importPath)) return; + if (isExternalRootModule(importPath)) { return; } const resolvedPath = resolve(importPath, context); @@ -167,7 +167,7 @@ module.exports = { if (!extension || !importPath.endsWith(`.${extension}`)) { // ignore type-only imports and exports - if (node.importKind === 'type' || node.exportKind === 'type') return; + if (node.importKind === 'type' || node.exportKind === 'type') { return; } const extensionRequired = isUseOfExtensionRequired(extension, isPackage); const extensionForbidden = isUseOfExtensionForbidden(extension); if (extensionRequired && !extensionForbidden) { diff --git a/src/rules/first.js b/src/rules/first.js index ebead6cf27..f8cc273a31 100644 --- a/src/rules/first.js +++ b/src/rules/first.js @@ -25,13 +25,13 @@ module.exports = { create(context) { function isPossibleDirective(node) { - return node.type === 'ExpressionStatement' && - node.expression.type === 'Literal' && - typeof node.expression.value === 'string'; + return node.type === 'ExpressionStatement' + && node.expression.type === 'Literal' + && typeof node.expression.value === 'string'; } return { - 'Program': function (n) { + Program(n) { const body = n.body; if (!body) { return; @@ -56,7 +56,7 @@ module.exports = { if (node.type === 'ImportDeclaration' || node.type === 'TSImportEqualsDeclaration') { if (absoluteFirst) { - if (/^\./.test(getImportValue(node))) { + if ((/^\./).test(getImportValue(node))) { anyRelative = true; } else if (anyRelative) { context.report({ @@ -67,7 +67,7 @@ module.exports = { } if (nonImportCount > 0) { for (const variable of context.getDeclaredVariables(node)) { - if (!shouldSort) break; + if (!shouldSort) { break; } const references = variable.references; if (references.length) { for (const reference of references) { @@ -90,7 +90,7 @@ module.exports = { nonImportCount++; } }); - if (!errorInfos.length) return; + if (!errorInfos.length) { return; } errorInfos.forEach(function (errorInfo, index) { const node = errorInfo.node; const infos = { @@ -112,26 +112,27 @@ module.exports = { const nodeSourceCode = String.prototype.slice.apply( originSourceCode, _errorInfo.range, ); - if (/\S/.test(nodeSourceCode[0])) { - return '\n' + nodeSourceCode; + if ((/\S/).test(nodeSourceCode[0])) { + return `\n${nodeSourceCode}`; } return nodeSourceCode; }).join(''); let insertFixer = null; let replaceSourceCode = ''; if (!lastLegalImp) { - insertSourceCode = - insertSourceCode.trim() + insertSourceCode.match(/^(\s+)/)[0]; + insertSourceCode = insertSourceCode.trim() + insertSourceCode.match(/^(\s+)/)[0]; } - insertFixer = lastLegalImp ? - fixer.insertTextAfter(lastLegalImp, insertSourceCode) : - fixer.insertTextBefore(body[0], insertSourceCode); + insertFixer = lastLegalImp + ? fixer.insertTextAfter(lastLegalImp, insertSourceCode) + : fixer.insertTextBefore(body[0], insertSourceCode); + const fixers = [insertFixer].concat(removeFixers); - fixers.forEach(function (computedFixer, i) { - replaceSourceCode += (originSourceCode.slice( + fixers.forEach((computedFixer, i) => { + replaceSourceCode += originSourceCode.slice( fixers[i - 1] ? fixers[i - 1].range[1] : 0, computedFixer.range[0], - ) + computedFixer.text); + ) + computedFixer.text; }); + return fixer.replaceTextRange(range, replaceSourceCode); }; } diff --git a/src/rules/group-exports.js b/src/rules/group-exports.js index 63af9d9141..7978130d34 100644 --- a/src/rules/group-exports.js +++ b/src/rules/group-exports.js @@ -98,7 +98,7 @@ function create(context) { 'Program:exit': function onExit() { // Report multiple `export` declarations (ES2015 modules) if (nodes.modules.set.size > 1) { - nodes.modules.set.forEach(node => { + nodes.modules.set.forEach((node) => { context.report({ node, message: errors[node.type], @@ -108,7 +108,7 @@ function create(context) { // Report multiple `aggregated exports` from the same module (ES2015 modules) flat(values(nodes.modules.sources) - .filter(nodesWithSource => Array.isArray(nodesWithSource) && nodesWithSource.length > 1)) + .filter((nodesWithSource) => Array.isArray(nodesWithSource) && nodesWithSource.length > 1)) .forEach((node) => { context.report({ node, @@ -118,7 +118,7 @@ function create(context) { // Report multiple `export type` declarations (FLOW ES2015 modules) if (nodes.types.set.size > 1) { - nodes.types.set.forEach(node => { + nodes.types.set.forEach((node) => { context.report({ node, message: errors[node.type], @@ -128,7 +128,7 @@ function create(context) { // Report multiple `aggregated type exports` from the same module (FLOW ES2015 modules) flat(values(nodes.types.sources) - .filter(nodesWithSource => Array.isArray(nodesWithSource) && nodesWithSource.length > 1)) + .filter((nodesWithSource) => Array.isArray(nodesWithSource) && nodesWithSource.length > 1)) .forEach((node) => { context.report({ node, @@ -138,7 +138,7 @@ function create(context) { // Report multiple `module.exports` assignments (CommonJS) if (nodes.commonjs.set.size > 1) { - nodes.commonjs.set.forEach(node => { + nodes.commonjs.set.forEach((node) => { context.report({ node, message: errors[node.type], diff --git a/src/rules/imports-first.js b/src/rules/imports-first.js index 07bb4633de..966367e99f 100644 --- a/src/rules/imports-first.js +++ b/src/rules/imports-first.js @@ -2,13 +2,14 @@ import docsUrl from '../docsUrl'; const first = require('./first'); -const newMeta = Object.assign({}, first.meta, { +const newMeta = { + ...first.meta, deprecated: true, docs: { category: 'Style guide', description: 'Replaced by `import/first`.', url: docsUrl('imports-first', '7b25c1cb95ee18acc1531002fd343e1e6031f9ed'), }, -}); +}; -module.exports = Object.assign({}, first, { meta: newMeta }); +module.exports = { ...first, meta: newMeta }; diff --git a/src/rules/max-dependencies.js b/src/rules/max-dependencies.js index 95f34176f5..488e906182 100644 --- a/src/rules/max-dependencies.js +++ b/src/rules/max-dependencies.js @@ -24,17 +24,17 @@ module.exports = { schema: [ { - 'type': 'object', - 'properties': { - 'max': { 'type': 'number' }, - 'ignoreTypeImports': { 'type': 'boolean' }, + type: 'object', + properties: { + max: { type: 'number' }, + ignoreTypeImports: { type: 'boolean' }, }, - 'additionalProperties': false, + additionalProperties: false, }, ], }, - create: context => { + create(context) { const { ignoreTypeImports = DEFAULT_IGNORE_TYPE_IMPORTS, } = context.options[0] || {}; @@ -42,15 +42,19 @@ module.exports = { const dependencies = new Set(); // keep track of dependencies let lastNode; // keep track of the last node to report on - return Object.assign({ - 'Program:exit': function () { + return { + 'Program:exit'() { countDependencies(dependencies, lastNode, context); }, - }, moduleVisitor((source, { importKind }) => { - if (importKind !== TYPE_IMPORT || !ignoreTypeImports) { - dependencies.add(source.value); - } - lastNode = source; - }, { commonjs: true })); + ...moduleVisitor( + (source, { importKind }) => { + if (importKind !== TYPE_IMPORT || !ignoreTypeImports) { + dependencies.add(source.value); + } + lastNode = source; + }, + { commonjs: true }, + ), + }; }, }; diff --git a/src/rules/named.js b/src/rules/named.js index 050f835056..e7fe4e4dce 100644 --- a/src/rules/named.js +++ b/src/rules/named.js @@ -67,12 +67,12 @@ module.exports = { if (!deepLookup.found) { if (deepLookup.path.length > 1) { const deepPath = deepLookup.path - .map(i => path.relative(path.dirname(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()), i.path)) + .map((i) => path.relative(path.dirname(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()), i.path)) .join(' -> '); context.report(im[key], `${name} not found via ${deepPath}`); } else { - context.report(im[key], name + ' not found in \'' + node.source.value + '\''); + context.report(im[key], `${name} not found in '${node.source.value}'`); } } }); @@ -121,12 +121,12 @@ module.exports = { if (!deepLookup.found) { if (deepLookup.path.length > 1) { const deepPath = deepLookup.path - .map(i => path.relative(path.dirname(context.getFilename()), i.path)) + .map((i) => path.relative(path.dirname(context.getFilename()), i.path)) .join(' -> '); context.report(im.key, `${im.key.name} not found via ${deepPath}`); } else { - context.report(im.key, im.key.name + ' not found in \'' + source.value + '\''); + context.report(im.key, `${im.key.name} not found in '${source.value}'`); } } }); diff --git a/src/rules/namespace.js b/src/rules/namespace.js index 3b6019da8d..77a3ea9077 100644 --- a/src/rules/namespace.js +++ b/src/rules/namespace.js @@ -4,12 +4,12 @@ import importDeclaration from '../importDeclaration'; import docsUrl from '../docsUrl'; function processBodyStatement(context, namespaces, declaration) { - if (declaration.type !== 'ImportDeclaration') return; + if (declaration.type !== 'ImportDeclaration') { return; } - if (declaration.specifiers.length === 0) return; + if (declaration.specifiers.length === 0) { return; } const imports = Exports.get(declaration.source.value, context); - if (imports == null) return null; + if (imports == null) { return null; } if (imports.errors.length > 0) { imports.reportErrors(context, declaration); @@ -18,25 +18,26 @@ function processBodyStatement(context, namespaces, declaration) { declaration.specifiers.forEach((specifier) => { switch (specifier.type) { - case 'ImportNamespaceSpecifier': - if (!imports.size) { - context.report( - specifier, - `No exported names found in module '${declaration.source.value}'.`, + 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; } - 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; - } + default: } }); } @@ -66,7 +67,6 @@ module.exports = { }, create: function namespaceRule(context) { - // read options const { allowComputed = false, @@ -81,7 +81,7 @@ module.exports = { return { // pick up all imports at body entry time, to properly respect hoisting Program({ body }) { - body.forEach(x => processBodyStatement(context, namespaces, x)); + body.forEach((x) => { processBodyStatement(context, namespaces, x); }); }, // same as above, but does not add names to local map @@ -89,7 +89,7 @@ module.exports = { const declaration = importDeclaration(context); const imports = Exports.get(declaration.source.value, context); - if (imports == null) return null; + if (imports == null) { return null; } if (imports.errors.length) { imports.reportErrors(context, declaration); @@ -107,9 +107,9 @@ module.exports = { // todo: check for possible redefinition MemberExpression(dereference) { - if (dereference.object.type !== 'Identifier') return; - if (!namespaces.has(dereference.object.name)) return; - if (declaredScope(context, dereference.object.name) !== 'module') return; + if (dereference.object.type !== 'Identifier') { return; } + if (!namespaces.has(dereference.object.name)) { return; } + if (declaredScope(context, dereference.object.name) !== 'module') { return; } if (dereference.parent.type === 'AssignmentExpression' && dereference.parent.left === dereference) { context.report( @@ -142,7 +142,7 @@ module.exports = { } const exported = namespace.get(dereference.property.name); - if (exported == null) return; + if (exported == null) { return; } // stash and pop namepath.push(dereference.property.name); @@ -152,18 +152,18 @@ module.exports = { }, VariableDeclarator({ id, init }) { - if (init == null) return; - if (init.type !== 'Identifier') return; - if (!namespaces.has(init.name)) return; + if (init == null) { return; } + if (init.type !== 'Identifier') { return; } + if (!namespaces.has(init.name)) { return; } // check for redefinition in intermediate scopes - if (declaredScope(context, init.name) !== 'module') return; + if (declaredScope(context, init.name) !== 'module') { return; } // DFS traverse child namespaces function testKey(pattern, namespace, path = [init.name]) { - if (!(namespace instanceof Exports)) return; + if (!(namespace instanceof Exports)) { return; } - if (pattern.type !== 'ObjectPattern') return; + if (pattern.type !== 'ObjectPattern') { return; } for (const property of pattern.properties) { if ( @@ -204,7 +204,7 @@ module.exports = { }, JSXMemberExpression({ object, property }) { - if (!namespaces.has(object.name)) return; + if (!namespaces.has(object.name)) { return; } const namespace = namespaces.get(object.name); if (!namespace.has(property.name)) { context.report({ diff --git a/src/rules/newline-after-import.js b/src/rules/newline-after-import.js index 36678bfc4e..c63bb21b24 100644 --- a/src/rules/newline-after-import.js +++ b/src/rules/newline-after-import.js @@ -63,22 +63,22 @@ module.exports = { fixable: 'whitespace', schema: [ { - 'type': 'object', - 'properties': { - 'count': { - 'type': 'integer', - 'minimum': 1, + type: 'object', + properties: { + count: { + type: 'integer', + minimum: 1, }, - 'considerComments': { 'type': 'boolean' }, + considerComments: { type: 'boolean' }, }, - 'additionalProperties': false, + additionalProperties: false, }, ], }, create(context) { let level = 0; const requireCalls = []; - const options = Object.assign({ count: 1, considerComments: false }, context.options[0]); + const options = { count: 1, considerComments: false, ...context.options[0] }; function checkForNewLine(node, nextNode, type) { if (isExportDefaultClass(nextNode) || isExportNameClass(nextNode)) { @@ -107,7 +107,7 @@ module.exports = { column, }, message: `Expected ${options.count} empty line${options.count > 1 ? 's' : ''} after ${type} statement not followed by another ${type}.`, - fix: fixer => fixer.insertTextAfter( + fix: (fixer) => fixer.insertTextAfter( node, '\n'.repeat(EXPECTED_LINE_DIFFERENCE - lineDifference), ), @@ -132,7 +132,7 @@ module.exports = { column, }, message: `Expected ${options.count} empty line${options.count > 1 ? 's' : ''} after import statement not followed by another import.`, - fix: fixer => fixer.insertTextAfter( + fix: (fixer) => fixer.insertTextAfter( node, '\n'.repeat(EXPECTED_LINE_DIFFERENCE - lineDifference), ), @@ -155,10 +155,9 @@ module.exports = { let nextComment; if (typeof parent.comments !== 'undefined' && options.considerComments) { - nextComment = parent.comments.find(o => o.loc.start.line === endLine + 1); + nextComment = parent.comments.find((o) => o.loc.start.line === endLine + 1); } - // skip "export import"s if (node.type === 'TSImportEqualsDeclaration' && node.isExport) { return; @@ -179,12 +178,12 @@ module.exports = { requireCalls.push(node); } }, - 'Program:exit': function () { + 'Program:exit'() { log('exit processing for', context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()); const scopeBody = getScopeBody(context.getScope()); log('got scope:', scopeBody); - requireCalls.forEach(function (node, index) { + requireCalls.forEach((node, index) => { const nodePosition = findNodeIndexInScopeBody(scopeBody, node); log('node position in scope:', nodePosition); @@ -196,8 +195,12 @@ module.exports = { return; } - if (nextStatement && - (!nextRequireCall || !containsNodeOrEqual(nextStatement, nextRequireCall))) { + if ( + nextStatement && ( + !nextRequireCall + || !containsNodeOrEqual(nextStatement, nextRequireCall) + ) + ) { checkForNewLine(statementWithRequireCall, nextStatement, 'require'); } diff --git a/src/rules/no-absolute-path.js b/src/rules/no-absolute-path.js index 19dae6b6fb..a5498ec765 100644 --- a/src/rules/no-absolute-path.js +++ b/src/rules/no-absolute-path.js @@ -21,12 +21,12 @@ module.exports = { context.report({ node: source, message: 'Do not import modules using an absolute path', - fix: fixer => { + fix(fixer) { const resolvedContext = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); // node.js and web imports work with posix style paths ("/") let relativePath = path.posix.relative(path.dirname(resolvedContext), source.value); if (!relativePath.startsWith('.')) { - relativePath = './' + relativePath; + relativePath = `./${relativePath}`; } return fixer.replaceText(source, JSON.stringify(relativePath)); }, @@ -34,7 +34,7 @@ module.exports = { } } - const options = Object.assign({ esmodule: true, commonjs: true }, context.options[0]); + const options = { esmodule: true, commonjs: true, ...context.options[0] }; return moduleVisitor(reportIfAbsolute, options); }, }; diff --git a/src/rules/no-amd.js b/src/rules/no-amd.js index 90359cd5fd..5edfe3e698 100644 --- a/src/rules/no-amd.js +++ b/src/rules/no-amd.js @@ -22,18 +22,17 @@ module.exports = { create(context) { return { - 'CallExpression': function (node) { - if (context.getScope().type !== 'module') return; + CallExpression(node) { + if (context.getScope().type !== 'module') { return; } - if (node.callee.type !== 'Identifier') return; - if (node.callee.name !== 'require' && - node.callee.name !== 'define') return; + if (node.callee.type !== 'Identifier') { return; } + if (node.callee.name !== 'require' && node.callee.name !== 'define') { return; } // todo: capture define((require, module, exports) => {}) form? - if (node.arguments.length !== 2) return; + if (node.arguments.length !== 2) { return; } const modules = node.arguments[0]; - if (modules.type !== 'ArrayExpression') return; + if (modules.type !== 'ArrayExpression') { return; } // todo: check second arg type? (identifier or callback) diff --git a/src/rules/no-anonymous-default-export.js b/src/rules/no-anonymous-default-export.js index d9edcc2b36..80950d550e 100644 --- a/src/rules/no-anonymous-default-export.js +++ b/src/rules/no-anonymous-default-export.js @@ -88,16 +88,16 @@ module.exports = { { type: 'object', properties: schemaProperties, - 'additionalProperties': false, + additionalProperties: false, }, ], }, create(context) { - const options = Object.assign({}, defaults, context.options[0]); + const options = { ...defaults, ...context.options[0] }; return { - 'ExportDefaultDeclaration': (node) => { + ExportDefaultDeclaration(node) { const def = defs[node.declaration.type]; // Recognized node type and allowed by configuration, diff --git a/src/rules/no-commonjs.js b/src/rules/no-commonjs.js index 7a35fc8a08..dde509222b 100644 --- a/src/rules/no-commonjs.js +++ b/src/rules/no-commonjs.js @@ -16,9 +16,9 @@ function normalizeLegacyOptions(options) { } function allowPrimitive(node, options) { - if (!options.allowPrimitiveModules) return false; - if (node.parent.type !== 'AssignmentExpression') return false; - return (node.parent.right.type !== 'ObjectExpression'); + if (!options.allowPrimitiveModules) { return false; } + if (node.parent.type !== 'AssignmentExpression') { return false; } + return node.parent.right.type !== 'ObjectExpression'; } function allowRequire(node, options) { @@ -40,14 +40,16 @@ function isConditional(node) { || node.type === 'TryStatement' || node.type === 'LogicalExpression' || node.type === 'ConditionalExpression' - ) return true; - if (node.parent) return isConditional(node.parent); + ) { + return true; + } + if (node.parent) { return isConditional(node.parent); } return false; } function isLiteralString(node) { - return (node.type === 'Literal' && typeof node.value === 'string') || - (node.type === 'TemplateLiteral' && node.expressions.length === 0); + return node.type === 'Literal' && typeof node.value === 'string' + || node.type === 'TemplateLiteral' && node.expressions.length === 0; } //------------------------------------------------------------------------------ @@ -58,9 +60,9 @@ const schemaString = { enum: ['allow-primitive-modules'] }; const schemaObject = { type: 'object', properties: { - allowPrimitiveModules: { 'type': 'boolean' }, - allowRequire: { 'type': 'boolean' }, - allowConditionalRequire: { 'type': 'boolean' }, + allowPrimitiveModules: { type: 'boolean' }, + allowRequire: { type: 'boolean' }, + allowConditionalRequire: { type: 'boolean' }, }, additionalProperties: false, }; @@ -95,11 +97,11 @@ module.exports = { return { - 'MemberExpression': function (node) { + MemberExpression(node) { // module.exports if (node.object.name === 'module' && node.property.name === 'exports') { - if (allowPrimitive(node, options)) return; + if (allowPrimitive(node, options)) { return; } context.report({ node, message: EXPORT_MESSAGE }); } @@ -107,25 +109,25 @@ module.exports = { if (node.object.name === 'exports') { const isInScope = context.getScope() .variables - .some(variable => variable.name === 'exports'); - if (! isInScope) { + .some((variable) => variable.name === 'exports'); + if (!isInScope) { context.report({ node, message: EXPORT_MESSAGE }); } } }, - 'CallExpression': function (call) { - if (!validateScope(context.getScope())) return; + CallExpression(call) { + if (!validateScope(context.getScope())) { return; } - if (call.callee.type !== 'Identifier') return; - if (call.callee.name !== 'require') return; + if (call.callee.type !== 'Identifier') { return; } + if (call.callee.name !== 'require') { return; } - if (call.arguments.length !== 1) return; - if (!isLiteralString(call.arguments[0])) return; + if (call.arguments.length !== 1) { return; } + if (!isLiteralString(call.arguments[0])) { return; } - if (allowRequire(call, options)) return; + if (allowRequire(call, options)) { return; } - if (allowConditionalRequire(call, options) && isConditional(call.parent)) return; + if (allowConditionalRequire(call, options) && isConditional(call.parent)) { return; } // keeping it simple: all 1-string-arg `require` calls are reported context.report({ diff --git a/src/rules/no-cycle.js b/src/rules/no-cycle.js index e12a81cea6..5b9d8c0709 100644 --- a/src/rules/no-cycle.js +++ b/src/rules/no-cycle.js @@ -48,7 +48,7 @@ module.exports = { create(context) { const myPath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); - if (myPath === '') return {}; // can't cycle-check a non-file + if (myPath === '') { return {}; } // can't cycle-check a non-file const options = context.options[0] || {}; const maxDepth = typeof options.maxDepth === 'number' ? options.maxDepth : Infinity; @@ -62,20 +62,23 @@ module.exports = { if (ignoreModule(sourceNode.value)) { return; // ignore external modules } - if (options.allowUnsafeDynamicCyclicDependency && ( - // Ignore `import()` - importer.type === 'ImportExpression' || - // `require()` calls are always checked (if possible) - (importer.type === 'CallExpression' && importer.callee.name !== 'require'))) { + if ( + options.allowUnsafeDynamicCyclicDependency && ( + // Ignore `import()` + importer.type === 'ImportExpression' + // `require()` calls are always checked (if possible) + || importer.type === 'CallExpression' && importer.callee.name !== 'require' + ) + ) { return; // cycle via dynamic import allowed by config } if ( importer.type === 'ImportDeclaration' && ( // import type { Foo } (TS and Flow) - importer.importKind === 'type' || + importer.importKind === 'type' // import { type Foo } (Flow) - importer.specifiers.every(({ importKind }) => importKind === 'type') + || importer.specifiers.every(({ importKind }) => importKind === 'type') ) ) { return; // ignore type imports @@ -91,25 +94,24 @@ module.exports = { return; // no-self-import territory } - const untraversed = [{ mget: () => imported, route:[] }]; + const untraversed = [{ mget: () => imported, route: [] }]; function detectCycle({ mget, route }) { const m = mget(); - if (m == null) return; - if (traversed.has(m.path)) return; + if (m == null) { return; } + if (traversed.has(m.path)) { return; } traversed.add(m.path); for (const [path, { getter, declarations }] of m.imports) { - if (traversed.has(path)) continue; - const toTraverse = [...declarations].filter(({ source, isOnlyImportingTypes }) => - !ignoreModule(source.value) && + if (traversed.has(path)) { continue; } + const toTraverse = [...declarations].filter(({ source, isOnlyImportingTypes }) => !ignoreModule(source.value) // Ignore only type imports - !isOnlyImportingTypes, + && !isOnlyImportingTypes, ); /* If cyclic dependency is allowed via dynamic import, skip checking if any module is imported dynamically */ - if (options.allowUnsafeDynamicCyclicDependency && toTraverse.some(d => d.dynamic)) return; + if (options.allowUnsafeDynamicCyclicDependency && toTraverse.some((d) => d.dynamic)) { return; } /* Only report as a cycle if there are any import declarations that are considered by @@ -121,7 +123,7 @@ module.exports = { b.ts: import type { Bar } from './a' */ - if (path === myPath && toTraverse.length > 0) return true; + if (path === myPath && toTraverse.length > 0) { return true; } if (route.length + 1 < maxDepth) { for (const { source } of toTraverse) { untraversed.push({ mget: getter, route: route.concat(source) }); @@ -133,9 +135,9 @@ module.exports = { while (untraversed.length > 0) { const next = untraversed.shift(); // bfs! if (detectCycle(next)) { - const message = (next.route.length > 0 + const message = next.route.length > 0 ? `Dependency cycle via ${routeString(next.route)}` - : 'Dependency cycle detected.'); + : 'Dependency cycle detected.'; context.report(importer, message); return; } @@ -143,7 +145,7 @@ module.exports = { } return Object.assign(moduleVisitor(checkSourceValue, context.options[0]), { - 'Program:exit': () => { + 'Program:exit'() { traversed.clear(); }, }); @@ -151,5 +153,5 @@ module.exports = { }; function routeString(route) { - return route.map(s => `${s.value}:${s.loc.start.line}`).join('=>'); + return route.map((s) => `${s.value}:${s.loc.start.line}`).join('=>'); } diff --git a/src/rules/no-default-export.js b/src/rules/no-default-export.js index 5fc8c40e4c..6e5a537485 100644 --- a/src/rules/no-default-export.js +++ b/src/rules/no-default-export.js @@ -27,7 +27,7 @@ module.exports = { }, ExportNamedDeclaration(node) { - node.specifiers.filter(specifier => (specifier.exported.name || specifier.exported.value) === '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/src/rules/no-deprecated.js b/src/rules/no-deprecated.js index 7a35a8e673..06eeff8ea7 100644 --- a/src/rules/no-deprecated.js +++ b/src/rules/no-deprecated.js @@ -3,13 +3,13 @@ import Exports from '../ExportMap'; import docsUrl from '../docsUrl'; function message(deprecation) { - return 'Deprecated' + (deprecation.description ? ': ' + deprecation.description : '.'); + return `Deprecated${deprecation.description ? `: ${deprecation.description}` : '.'}`; } function getDeprecation(metadata) { - if (!metadata || !metadata.doc) return; + if (!metadata || !metadata.doc) { return; } - return metadata.doc.tags.find(t => t.title === 'deprecated'); + return metadata.doc.tags.find((t) => t.title === 'deprecated'); } module.exports = { @@ -28,13 +28,13 @@ module.exports = { const namespaces = new Map(); function checkSpecifiers(node) { - if (node.type !== 'ImportDeclaration') return; - if (node.source == null) return; // local export, ignore + if (node.type !== 'ImportDeclaration') { return; } + if (node.source == null) { return; } // local export, ignore const imports = Exports.get(node.source.value, context); - if (imports == null) return; + if (imports == null) { return; } - const moduleDeprecation = imports.doc && imports.doc.tags.find(t => t.title === 'deprecated'); + const moduleDeprecation = imports.doc && imports.doc.tags.find((t) => t.title === 'deprecated'); if (moduleDeprecation) { context.report({ node, message: message(moduleDeprecation) }); } @@ -48,35 +48,34 @@ module.exports = { let imported; let local; switch (im.type) { + case 'ImportNamespaceSpecifier': { + if (!imports.size) { return; } + namespaces.set(im.local.name, imports); + return; + } - case 'ImportNamespaceSpecifier':{ - if (!imports.size) return; - namespaces.set(im.local.name, imports); - return; - } - - case 'ImportDefaultSpecifier': - imported = 'default'; - local = im.local.name; - break; + case 'ImportDefaultSpecifier': + imported = 'default'; + local = im.local.name; + break; - case 'ImportSpecifier': - imported = im.imported.name; - local = im.local.name; - break; + case 'ImportSpecifier': + imported = im.imported.name; + local = im.local.name; + break; - default: return; // can't handle this one + default: return; // can't handle this one } // unknown thing can't be deprecated const exported = imports.get(imported); - if (exported == null) return; + if (exported == null) { return; } // capture import of deep namespace - if (exported.namespace) namespaces.set(local, exported.namespace); + if (exported.namespace) { namespaces.set(local, exported.namespace); } const deprecation = getDeprecation(imports.get(imported)); - if (!deprecation) return; + if (!deprecation) { return; } context.report({ node: im, message: message(deprecation) }); @@ -86,44 +85,42 @@ module.exports = { } return { - 'Program': ({ body }) => body.forEach(checkSpecifiers), + Program: ({ body }) => body.forEach(checkSpecifiers), - 'Identifier': function (node) { + Identifier(node) { if (node.parent.type === 'MemberExpression' && node.parent.property === node) { return; // handled by MemberExpression } // ignore specifier identifiers - if (node.parent.type.slice(0, 6) === 'Import') return; + if (node.parent.type.slice(0, 6) === 'Import') { return; } - if (!deprecated.has(node.name)) return; + if (!deprecated.has(node.name)) { return; } - if (declaredScope(context, node.name) !== 'module') return; + if (declaredScope(context, node.name) !== 'module') { return; } context.report({ node, message: message(deprecated.get(node.name)), }); }, - 'MemberExpression': function (dereference) { - if (dereference.object.type !== 'Identifier') return; - if (!namespaces.has(dereference.object.name)) return; + MemberExpression(dereference) { + if (dereference.object.type !== 'Identifier') { return; } + if (!namespaces.has(dereference.object.name)) { return; } - if (declaredScope(context, dereference.object.name) !== 'module') return; + if (declaredScope(context, dereference.object.name) !== 'module') { return; } // go deep let namespace = namespaces.get(dereference.object.name); const namepath = [dereference.object.name]; // while property is namespace and parent is member expression, keep validating - while (namespace instanceof Exports && - dereference.type === 'MemberExpression') { - + while (namespace instanceof Exports && dereference.type === 'MemberExpression') { // ignore computed parts for now - if (dereference.computed) return; + if (dereference.computed) { return; } const metadata = namespace.get(dereference.property.name); - if (!metadata) break; + if (!metadata) { break; } const deprecation = getDeprecation(metadata); if (deprecation) { diff --git a/src/rules/no-duplicates.js b/src/rules/no-duplicates.js index 15515e6757..76bf187b2e 100644 --- a/src/rules/no-duplicates.js +++ b/src/rules/no-duplicates.js @@ -62,16 +62,16 @@ function getFix(first, rest, sourceCode, context) { // Leave it to the user to handle comments. Also skip `import * as ns from // './foo'` imports, since they cannot be merged into another import. - const restWithoutComments = rest.filter(node => !( - hasProblematicComments(node, sourceCode) || - hasNamespace(node) + const restWithoutComments = rest.filter((node) => !( + hasProblematicComments(node, sourceCode) + || hasNamespace(node) )); const specifiers = restWithoutComments - .map(node => { + .map((node) => { const tokens = sourceCode.getTokens(node); - const openBrace = tokens.find(token => isPunctuator(token, '{')); - const closeBrace = tokens.find(token => isPunctuator(token, '}')); + const openBrace = tokens.find((token) => isPunctuator(token, '{')); + const closeBrace = tokens.find((token) => isPunctuator(token, '}')); if (openBrace == null || closeBrace == null) { return undefined; @@ -85,10 +85,9 @@ function getFix(first, rest, sourceCode, context) { }) .filter(Boolean); - const unnecessaryImports = restWithoutComments.filter(node => - !hasSpecifiers(node) && - !hasNamespace(node) && - !specifiers.some(specifier => specifier.importNode === node), + const unnecessaryImports = restWithoutComments.filter((node) => !hasSpecifiers(node) + && !hasNamespace(node) + && !specifiers.some((specifier) => specifier.importNode === node), ); const shouldAddDefault = getDefaultImportName(first) == null && defaultImportNames.size === 1; @@ -99,16 +98,14 @@ function getFix(first, rest, sourceCode, context) { return undefined; } - return fixer => { + return (fixer) => { const tokens = sourceCode.getTokens(first); - const openBrace = tokens.find(token => isPunctuator(token, '{')); - const closeBrace = tokens.find(token => isPunctuator(token, '}')); + const openBrace = tokens.find((token) => isPunctuator(token, '{')); + const closeBrace = tokens.find((token) => isPunctuator(token, '}')); const firstToken = sourceCode.getFirstToken(first); const [defaultImportName] = defaultImportNames; - const firstHasTrailingComma = - closeBrace != null && - isPunctuator(sourceCode.getTokenBefore(closeBrace), ','); + const firstHasTrailingComma = closeBrace != null && isPunctuator(sourceCode.getTokenBefore(closeBrace), ','); const firstIsEmpty = !hasSpecifiers(first); const firstExistingIdentifiers = firstIsEmpty ? new Set() @@ -214,21 +211,21 @@ function isPunctuator(node, value) { // Get the name of the default import of `node`, if any. function getDefaultImportName(node) { const defaultSpecifier = node.specifiers - .find(specifier => specifier.type === 'ImportDefaultSpecifier'); + .find((specifier) => specifier.type === 'ImportDefaultSpecifier'); return defaultSpecifier != null ? defaultSpecifier.local.name : undefined; } // Checks whether `node` has a namespace import. function hasNamespace(node) { const specifiers = node.specifiers - .filter(specifier => specifier.type === 'ImportNamespaceSpecifier'); + .filter((specifier) => specifier.type === 'ImportNamespaceSpecifier'); return specifiers.length > 0; } // Checks whether `node` has any non-default specifiers. function hasSpecifiers(node) { const specifiers = node.specifiers - .filter(specifier => specifier.type === 'ImportSpecifier'); + .filter((specifier) => specifier.type === 'ImportSpecifier'); return specifiers.length > 0; } @@ -236,9 +233,9 @@ function hasSpecifiers(node) { // duplicate imports, so skip imports with comments when autofixing. function hasProblematicComments(node, sourceCode) { return ( - hasCommentBefore(node, sourceCode) || - hasCommentAfter(node, sourceCode) || - hasCommentInsideNonSpecifiers(node, sourceCode) + hasCommentBefore(node, sourceCode) + || hasCommentAfter(node, sourceCode) + || hasCommentInsideNonSpecifiers(node, sourceCode) ); } @@ -246,29 +243,29 @@ function hasProblematicComments(node, sourceCode) { // the same line as `node` (starts). function hasCommentBefore(node, sourceCode) { return sourceCode.getCommentsBefore(node) - .some(comment => comment.loc.end.line >= node.loc.start.line - 1); + .some((comment) => comment.loc.end.line >= node.loc.start.line - 1); } // Checks whether `node` has a comment (that starts) on the same line as `node` // (ends). function hasCommentAfter(node, sourceCode) { return sourceCode.getCommentsAfter(node) - .some(comment => comment.loc.start.line === node.loc.end.line); + .some((comment) => comment.loc.start.line === node.loc.end.line); } // Checks whether `node` has any comments _inside,_ except inside the `{...}` // part (if any). function hasCommentInsideNonSpecifiers(node, sourceCode) { const tokens = sourceCode.getTokens(node); - const openBraceIndex = tokens.findIndex(token => isPunctuator(token, '{')); - const closeBraceIndex = tokens.findIndex(token => isPunctuator(token, '}')); + const openBraceIndex = tokens.findIndex((token) => isPunctuator(token, '{')); + const closeBraceIndex = tokens.findIndex((token) => isPunctuator(token, '}')); // Slice away the first token, since we're no looking for comments _before_ // `node` (only inside). If there's a `{...}` part, look for comments before // the `{`, but not before the `}` (hence the `+1`s). const someTokens = openBraceIndex >= 0 && closeBraceIndex >= 0 ? tokens.slice(1, openBraceIndex + 1).concat(tokens.slice(closeBraceIndex + 1)) : tokens.slice(1); - return someTokens.some(token => sourceCode.getCommentsBefore(token).length > 0); + return someTokens.some((token) => sourceCode.getCommentsBefore(token).length > 0); } module.exports = { @@ -298,16 +295,16 @@ module.exports = { create(context) { // Prepare the resolver from options. - const considerQueryStringOption = context.options[0] && - context.options[0]['considerQueryString']; - const defaultResolver = sourcePath => resolve(sourcePath, context) || sourcePath; - const resolver = considerQueryStringOption ? (sourcePath => { + const considerQueryStringOption = context.options[0] + && context.options[0].considerQueryString; + const defaultResolver = (sourcePath) => resolve(sourcePath, context) || sourcePath; + const resolver = considerQueryStringOption ? (sourcePath) => { const parts = sourcePath.match(/^([^?]*)\?(.*)$/); if (!parts) { return defaultResolver(sourcePath); } - return defaultResolver(parts[1]) + '?' + parts[2]; - }) : defaultResolver; + return `${defaultResolver(parts[1])}?${parts[2]}`; + } : defaultResolver; const moduleMaps = new Map(); @@ -344,7 +341,7 @@ module.exports = { } }, - 'Program:exit': function () { + 'Program:exit'() { for (const map of moduleMaps.values()) { checkImports(map.imported, context); checkImports(map.nsImported, context); diff --git a/src/rules/no-dynamic-require.js b/src/rules/no-dynamic-require.js index f334adec67..f8b369a70f 100644 --- a/src/rules/no-dynamic-require.js +++ b/src/rules/no-dynamic-require.js @@ -1,22 +1,22 @@ import docsUrl from '../docsUrl'; function isRequire(node) { - return node && - node.callee && - node.callee.type === 'Identifier' && - node.callee.name === 'require' && - node.arguments.length >= 1; + return node + && node.callee + && node.callee.type === 'Identifier' + && node.callee.name === 'require' + && node.arguments.length >= 1; } function isDynamicImport(node) { - return node && - node.callee && - node.callee.type === 'Import'; + return node + && node.callee + && node.callee.type === 'Import'; } function isStaticValue(arg) { - return arg.type === 'Literal' || - (arg.type === 'TemplateLiteral' && arg.expressions.length === 0); + return arg.type === 'Literal' + || arg.type === 'TemplateLiteral' && arg.expressions.length === 0; } const dynamicImportErrorMessage = 'Calls to import() should use string literals'; diff --git a/src/rules/no-empty-named-blocks.js b/src/rules/no-empty-named-blocks.js index 25567b08f8..3ec1501b8f 100644 --- a/src/rules/no-empty-named-blocks.js +++ b/src/rules/no-empty-named-blocks.js @@ -8,7 +8,7 @@ function getEmptyBlockRange(tokens, index) { const end = nextToken.range[1]; // Remove block tokens and the previous comma - if (prevToken.value === ','|| prevToken.value === 'type' || prevToken.value === 'typeof') { + if (prevToken.value === ',' || prevToken.value === 'type' || prevToken.value === 'typeof') { start = prevToken.range[0]; } @@ -33,15 +33,13 @@ module.exports = { return { ImportDeclaration(node) { - if (!node.specifiers.some(x => x.type === 'ImportSpecifier')) { + if (!node.specifiers.some((x) => x.type === 'ImportSpecifier')) { importsWithoutNameds.push(node); } }, - 'Program:exit': function (program) { - const importsTokens = importsWithoutNameds.map((node) => { - return [node, program.tokens.filter(x => x.range[0] >= node.range[0] && x.range[1] <= node.range[1])]; - }); + 'Program:exit'(program) { + const importsTokens = importsWithoutNameds.map((node) => [node, program.tokens.filter((x) => x.range[0] >= node.range[0] && x.range[1] <= node.range[1])]); importsTokens.forEach(([node, tokens]) => { tokens.forEach((token) => { @@ -49,12 +47,11 @@ module.exports = { const nextToken = program.tokens[idx + 1]; if (nextToken && token.value === '{' && nextToken.value === '}') { - const hasOtherIdentifiers = tokens.some((token) => ( - token.type === 'Identifier' + const hasOtherIdentifiers = tokens.some((token) => token.type === 'Identifier' && token.value !== 'from' && token.value !== 'type' - && token.value !== 'typeof' - )); + && token.value !== 'typeof', + ); // If it has no other identifiers it's the only thing in the import, so we can either remove the import // completely or transform it in a side-effects only import @@ -76,8 +73,8 @@ module.exports = { // Remove the empty block and the 'from' token, leaving the import only for its side // effects, e.g. `import 'mod'` const sourceCode = context.getSourceCode(); - const fromToken = program.tokens.find(t => t.value === 'from'); - const importToken = program.tokens.find(t => t.value === 'import'); + const fromToken = program.tokens.find((t) => t.value === 'from'); + const importToken = program.tokens.find((t) => t.value === 'import'); const hasSpaceAfterFrom = sourceCode.isSpaceBetween(fromToken, sourceCode.getTokenAfter(fromToken)); const hasSpaceAfterImport = sourceCode.isSpaceBetween(importToken, sourceCode.getTokenAfter(fromToken)); diff --git a/src/rules/no-extraneous-dependencies.js b/src/rules/no-extraneous-dependencies.js index a149ca6599..0408e0866d 100644 --- a/src/rules/no-extraneous-dependencies.js +++ b/src/rules/no-extraneous-dependencies.js @@ -64,18 +64,18 @@ function getDependencies(context, packageDir) { if (!Array.isArray(packageDir)) { paths = [path.resolve(packageDir)]; } else { - paths = packageDir.map(dir => path.resolve(dir)); + paths = packageDir.map((dir) => path.resolve(dir)); } } if (paths.length > 0) { // use rule config to find package.json - paths.forEach(dir => { + paths.forEach((dir) => { const packageJsonPath = path.join(dir, 'package.json'); const _packageContent = getPackageDepFields(packageJsonPath, true); - Object.keys(packageContent).forEach(depsKey => - Object.assign(packageContent[depsKey], _packageContent[depsKey]), - ); + Object.keys(packageContent).forEach((depsKey) => { + Object.assign(packageContent[depsKey], _packageContent[depsKey]); + }); }); } else { const packageJsonPath = pkgUp({ @@ -110,7 +110,7 @@ function getDependencies(context, packageDir) { } if (e.name === 'JSONError' || e instanceof SyntaxError) { context.report({ - message: 'The package.json file could not be parsed: ' + e.message, + message: `The package.json file could not be parsed: ${e.message}`, loc: { line: 0, column: 0 }, }); } @@ -120,8 +120,7 @@ function getDependencies(context, packageDir) { } function missingErrorMessage(packageName) { - return `'${packageName}' should be listed in the project's dependencies. ` + - `Run 'npm i -S ${packageName}' to add it`; + return `'${packageName}' should be listed in the project's dependencies. Run 'npm i -S ${packageName}' to add it`; } function devDepErrorMessage(packageName) { @@ -129,8 +128,7 @@ function devDepErrorMessage(packageName) { } function optDepErrorMessage(packageName) { - return `'${packageName}' should be listed in the project's dependencies, ` + - `not optionalDependencies.`; + return `'${packageName}' should be listed in the project's dependencies, not optionalDependencies.`; } function getModuleOriginalName(name) { @@ -162,27 +160,24 @@ function checkDependencyDeclaration(deps, packageName, declarationStatus) { } }); - return packageHierarchy.reduce((result, ancestorName) => { - return { - isInDeps: result.isInDeps || deps.dependencies[ancestorName] !== undefined, - isInDevDeps: result.isInDevDeps || deps.devDependencies[ancestorName] !== undefined, - isInOptDeps: result.isInOptDeps || deps.optionalDependencies[ancestorName] !== undefined, - isInPeerDeps: result.isInPeerDeps || deps.peerDependencies[ancestorName] !== undefined, - isInBundledDeps: + return packageHierarchy.reduce((result, ancestorName) => ({ + isInDeps: result.isInDeps || deps.dependencies[ancestorName] !== undefined, + isInDevDeps: result.isInDevDeps || deps.devDependencies[ancestorName] !== undefined, + isInOptDeps: result.isInOptDeps || deps.optionalDependencies[ancestorName] !== undefined, + isInPeerDeps: result.isInPeerDeps || deps.peerDependencies[ancestorName] !== undefined, + isInBundledDeps: result.isInBundledDeps || deps.bundledDependencies.indexOf(ancestorName) !== -1, - }; - }, newDeclarationStatus); + }), newDeclarationStatus); } function reportIfMissing(context, deps, depsOptions, node, name) { // Do not report when importing types unless option is enabled if ( - !depsOptions.verifyTypeImports && - (node.importKind === 'type' || node.importKind === 'typeof' || - ( - Array.isArray(node.specifiers) && - node.specifiers.length && - node.specifiers.every((specifier) => specifier.importKind === 'type' || specifier.importKind === 'typeof')) + !depsOptions.verifyTypeImports + && ( + node.importKind === 'type' + || node.importKind === 'typeof' + || Array.isArray(node.specifiers) && node.specifiers.length && node.specifiers.every((specifier) => specifier.importKind === 'type' || specifier.importKind === 'typeof') ) ) { return; @@ -204,11 +199,11 @@ function reportIfMissing(context, deps, depsOptions, node, name) { let declarationStatus = checkDependencyDeclaration(deps, importPackageName); if ( - declarationStatus.isInDeps || - (depsOptions.allowDevDeps && declarationStatus.isInDevDeps) || - (depsOptions.allowPeerDeps && declarationStatus.isInPeerDeps) || - (depsOptions.allowOptDeps && declarationStatus.isInOptDeps) || - (depsOptions.allowBundledDeps && declarationStatus.isInBundledDeps) + declarationStatus.isInDeps + || depsOptions.allowDevDeps && declarationStatus.isInDevDeps + || depsOptions.allowPeerDeps && declarationStatus.isInPeerDeps + || depsOptions.allowOptDeps && declarationStatus.isInOptDeps + || depsOptions.allowBundledDeps && declarationStatus.isInBundledDeps ) { return; } @@ -220,11 +215,11 @@ function reportIfMissing(context, deps, depsOptions, node, name) { declarationStatus = checkDependencyDeclaration(deps, realPackageName, declarationStatus); if ( - declarationStatus.isInDeps || - (depsOptions.allowDevDeps && declarationStatus.isInDevDeps) || - (depsOptions.allowPeerDeps && declarationStatus.isInPeerDeps) || - (depsOptions.allowOptDeps && declarationStatus.isInOptDeps) || - (depsOptions.allowBundledDeps && declarationStatus.isInBundledDeps) + declarationStatus.isInDeps + || depsOptions.allowDevDeps && declarationStatus.isInDevDeps + || depsOptions.allowPeerDeps && declarationStatus.isInPeerDeps + || depsOptions.allowOptDeps && declarationStatus.isInOptDeps + || depsOptions.allowBundledDeps && declarationStatus.isInBundledDeps ) { return; } @@ -249,10 +244,9 @@ function testConfig(config, filename) { return config; } // Array of globs. - return config.some(c => ( - minimatch(filename, c) || - minimatch(filename, path.join(process.cwd(), c)) - )); + return config.some((c) => minimatch(filename, c) + || minimatch(filename, path.join(process.cwd(), c)), + ); } module.exports = { @@ -266,17 +260,17 @@ module.exports = { schema: [ { - 'type': 'object', - 'properties': { - 'devDependencies': { 'type': ['boolean', 'array'] }, - 'optionalDependencies': { 'type': ['boolean', 'array'] }, - 'peerDependencies': { 'type': ['boolean', 'array'] }, - 'bundledDependencies': { 'type': ['boolean', 'array'] }, - 'packageDir': { 'type': ['string', 'array'] }, - 'includeInternal': { 'type': ['boolean'] }, - 'includeTypes': { 'type': ['boolean'] }, + type: 'object', + properties: { + devDependencies: { type: ['boolean', 'array'] }, + optionalDependencies: { type: ['boolean', 'array'] }, + peerDependencies: { type: ['boolean', 'array'] }, + bundledDependencies: { type: ['boolean', 'array'] }, + packageDir: { type: ['string', 'array'] }, + includeInternal: { type: ['boolean'] }, + includeTypes: { type: ['boolean'] }, }, - 'additionalProperties': false, + additionalProperties: false, }, ], }, @@ -300,7 +294,7 @@ module.exports = { }, { commonjs: true }); }, - 'Program:exit': () => { + 'Program:exit'() { depFieldCache.clear(); }, }; diff --git a/src/rules/no-import-module-exports.js b/src/rules/no-import-module-exports.js index 5a91acd07d..bc4605c39d 100644 --- a/src/rules/no-import-module-exports.js +++ b/src/rules/no-import-module-exports.js @@ -16,12 +16,12 @@ function getEntryPoint(context) { function findScope(context, identifier) { const { scopeManager } = context.getSourceCode(); - return scopeManager && scopeManager.scopes.slice().reverse().find((scope) => scope.variables.some(variable => variable.identifiers.some((node) => node.name === identifier))); + return scopeManager && scopeManager.scopes.slice().reverse().find((scope) => scope.variables.some((variable) => variable.identifiers.some((node) => node.name === identifier))); } function findDefinition(objectScope, identifier) { - const variable = objectScope.variables.find(variable => variable.name === identifier); - return variable.defs.find(def => def.name.name === identifier); + const variable = objectScope.variables.find((variable) => variable.name === identifier); + return variable.defs.find((def) => def.name.name === identifier); } module.exports = { @@ -35,11 +35,11 @@ module.exports = { fixable: 'code', schema: [ { - 'type': 'object', - 'properties': { - 'exceptions': { 'type': 'array' }, + type: 'object', + properties: { + exceptions: { type: 'array' }, }, - 'additionalProperties': false, + additionalProperties: false, }, ], }, @@ -58,14 +58,13 @@ module.exports = { const variableDefinition = objectScope && findDefinition(objectScope, node.object.name); const isImportBinding = variableDefinition && variableDefinition.type === 'ImportBinding'; const hasCJSExportReference = hasKeywords && (!objectScope || objectScope.type === 'module'); - const isException = !!options.exceptions && options.exceptions.some(glob => minimatch(fileName, glob)); + const isException = !!options.exceptions && options.exceptions.some((glob) => minimatch(fileName, glob)); if (isIdentifier && hasCJSExportReference && !isEntryPoint && !isException && !isImportBinding) { - importDeclarations.forEach(importDeclaration => { + importDeclarations.forEach((importDeclaration) => { context.report({ node: importDeclaration, - message: `Cannot use import declarations in modules that export using ` + - `CommonJS (module.exports = 'foo' or exports.bar = 'hi')`, + message: `Cannot use import declarations in modules that export using CommonJS (module.exports = 'foo' or exports.bar = 'hi')`, }); }); alreadyReported = true; diff --git a/src/rules/no-internal-modules.js b/src/rules/no-internal-modules.js index 2416c1ce3f..687193f5c5 100644 --- a/src/rules/no-internal-modules.js +++ b/src/rules/no-internal-modules.js @@ -48,8 +48,8 @@ module.exports = { create: function noReachingInside(context) { const options = context.options[0] || {}; - const allowRegexps = (options.allow || []).map(p => minimatch.makeRe(p)); - const forbidRegexps = (options.forbid || []).map(p => minimatch.makeRe(p)); + const allowRegexps = (options.allow || []).map((p) => minimatch.makeRe(p)); + const forbidRegexps = (options.forbid || []).map((p) => minimatch.makeRe(p)); // minimatch patterns are expected to use / path separators, like import // statements, so normalize paths to use the same @@ -73,29 +73,29 @@ module.exports = { // test if reaching to this destination is allowed function reachingAllowed(importPath) { - return allowRegexps.some(re => re.test(importPath)); + return allowRegexps.some((re) => re.test(importPath)); } // test if reaching to this destination is forbidden function reachingForbidden(importPath) { - return forbidRegexps.some(re => re.test(importPath)); + return forbidRegexps.some((re) => re.test(importPath)); } function isAllowViolation(importPath) { const steps = toSteps(importPath); - const nonScopeSteps = steps.filter(step => step.indexOf('@') !== 0); - if (nonScopeSteps.length <= 1) return false; + const nonScopeSteps = steps.filter((step) => step.indexOf('@') !== 0); + if (nonScopeSteps.length <= 1) { return false; } // before trying to resolve, see if the raw import (with relative // segments resolved) matches an allowed pattern const justSteps = steps.join('/'); - if (reachingAllowed(justSteps) || reachingAllowed(`/${justSteps}`)) return false; + if (reachingAllowed(justSteps) || reachingAllowed(`/${justSteps}`)) { return false; } // if the import statement doesn't match directly, try to match the // resolved path if the import is resolvable const resolved = resolve(importPath, context); - if (!resolved || reachingAllowed(normalizeSep(resolved))) return false; + if (!resolved || reachingAllowed(normalizeSep(resolved))) { return false; } // this import was not allowed by the allowed paths, and reaches // so it is a violation @@ -109,12 +109,12 @@ module.exports = { // segments resolved) matches a forbidden pattern const justSteps = steps.join('/'); - if (reachingForbidden(justSteps) || reachingForbidden(`/${justSteps}`)) return true; + if (reachingForbidden(justSteps) || reachingForbidden(`/${justSteps}`)) { return true; } // if the import statement doesn't match directly, try to match the // resolved path if the import is resolvable const resolved = resolve(importPath, context); - if (resolved && reachingForbidden(normalizeSep(resolved))) return true; + if (resolved && reachingForbidden(normalizeSep(resolved))) { return true; } // this import was not forbidden by the forbidden paths so it is not a violation return false; @@ -125,8 +125,9 @@ module.exports = { function checkImportForReaching(importPath, node) { const potentialViolationTypes = ['parent', 'index', 'sibling', 'external', 'internal']; - if (potentialViolationTypes.indexOf(importType(importPath, context)) !== -1 && - isReachViolation(importPath) + if ( + potentialViolationTypes.indexOf(importType(importPath, context)) !== -1 + && isReachViolation(importPath) ) { context.report({ node, @@ -135,8 +136,11 @@ module.exports = { } } - return moduleVisitor((source) => { - checkImportForReaching(source.value, source); - }, { commonjs: true }); + return moduleVisitor( + (source) => { + checkImportForReaching(source.value, source); + }, + { commonjs: true }, + ); }, }; diff --git a/src/rules/no-mutable-exports.js b/src/rules/no-mutable-exports.js index 75a321b62a..433d64e167 100644 --- a/src/rules/no-mutable-exports.js +++ b/src/rules/no-mutable-exports.js @@ -52,8 +52,8 @@ module.exports = { } return { - 'ExportDefaultDeclaration': handleExportDefault, - 'ExportNamedDeclaration': handleExportNamed, + ExportDefaultDeclaration: handleExportDefault, + ExportNamedDeclaration: handleExportNamed, }; }, }; diff --git a/src/rules/no-named-as-default-member.js b/src/rules/no-named-as-default-member.js index 0fb0927249..e00a4cbc87 100644 --- a/src/rules/no-named-as-default-member.js +++ b/src/rules/no-named-as-default-member.js @@ -24,81 +24,68 @@ module.exports = { }, create(context) { - const fileImports = new Map(); const allPropertyLookups = new Map(); - function handleImportDefault(node) { - const declaration = importDeclaration(context); - const exportMap = Exports.get(declaration.source.value, context); - if (exportMap == null) return; - - if (exportMap.errors.length) { - exportMap.reportErrors(context, declaration); - return; - } - - fileImports.set(node.local.name, { - exportMap, - sourcePath: declaration.source.value, - }); - } - function storePropertyLookup(objectName, propName, node) { const lookups = allPropertyLookups.get(objectName) || []; lookups.push({ node, propName }); allPropertyLookups.set(objectName, lookups); } - function handlePropLookup(node) { - const objectName = node.object.name; - const propName = node.property.name; - storePropertyLookup(objectName, propName, node); - } + return { + ImportDefaultSpecifier(node) { + const declaration = importDeclaration(context); + const exportMap = Exports.get(declaration.source.value, context); + if (exportMap == null) { return; } - function handleDestructuringAssignment(node) { - const isDestructure = ( - node.id.type === 'ObjectPattern' && - node.init != null && - node.init.type === 'Identifier' - ); - if (!isDestructure) return; + if (exportMap.errors.length) { + exportMap.reportErrors(context, declaration); + return; + } - const objectName = node.init.name; - for (const { key } of node.id.properties) { - if (key == null) continue; // true for rest properties - storePropertyLookup(objectName, key.name, key); - } - } + fileImports.set(node.local.name, { + exportMap, + sourcePath: declaration.source.value, + }); + }, - function handleProgramExit() { - allPropertyLookups.forEach((lookups, objectName) => { - const fileImport = fileImports.get(objectName); - if (fileImport == null) return; + MemberExpression(node) { + const objectName = node.object.name; + const propName = node.property.name; + storePropertyLookup(objectName, propName, node); + }, - for (const { propName, node } of lookups) { - // the default import can have a "default" property - if (propName === 'default') continue; - if (!fileImport.exportMap.namespace.has(propName)) continue; + VariableDeclarator(node) { + const isDestructure = node.id.type === 'ObjectPattern' + && node.init != null + && node.init.type === 'Identifier'; + if (!isDestructure) { return; } - context.report({ - node, - message: ( - `Caution: \`${objectName}\` also has a named export ` + - `\`${propName}\`. Check if you meant to write ` + - `\`import {${propName}} from '${fileImport.sourcePath}'\` ` + - 'instead.' - ), - }); + const objectName = node.init.name; + for (const { key } of node.id.properties) { + if (key == null) { continue; } // true for rest properties + storePropertyLookup(objectName, key.name, key); } - }); - } + }, - return { - 'ImportDefaultSpecifier': handleImportDefault, - 'MemberExpression': handlePropLookup, - 'VariableDeclarator': handleDestructuringAssignment, - 'Program:exit': handleProgramExit, + 'Program:exit'() { + allPropertyLookups.forEach((lookups, objectName) => { + const fileImport = fileImports.get(objectName); + if (fileImport == null) { return; } + + for (const { propName, node } of lookups) { + // the default import can have a "default" property + if (propName === 'default') { continue; } + if (!fileImport.exportMap.namespace.has(propName)) { continue; } + + context.report({ + node, + message: `Caution: \`${objectName}\` also has a named export \`${propName}\`. Check if you meant to write \`import {${propName}} from '${fileImport.sourcePath}'\` instead.`, + }); + } + }); + }, }; }, }; diff --git a/src/rules/no-named-as-default.js b/src/rules/no-named-as-default.js index c3a35ff64a..40b1e175b2 100644 --- a/src/rules/no-named-as-default.js +++ b/src/rules/no-named-as-default.js @@ -16,30 +16,30 @@ module.exports = { create(context) { function checkDefault(nameKey, defaultSpecifier) { // #566: default is a valid specifier - if (defaultSpecifier[nameKey].name === 'default') return; + if (defaultSpecifier[nameKey].name === 'default') { return; } const declaration = importDeclaration(context); const imports = Exports.get(declaration.source.value, context); - if (imports == null) return; + if (imports == null) { return; } if (imports.errors.length) { imports.reportErrors(context, declaration); return; } - if (imports.has('default') && - imports.has(defaultSpecifier[nameKey].name)) { + if (imports.has('default') && imports.has(defaultSpecifier[nameKey].name)) { - context.report(defaultSpecifier, - 'Using exported name \'' + defaultSpecifier[nameKey].name + - '\' as identifier for default export.'); + context.report( + defaultSpecifier, + `Using exported name '${defaultSpecifier[nameKey].name}' as identifier for default export.`, + ); } } return { - 'ImportDefaultSpecifier': checkDefault.bind(null, 'local'), - 'ExportDefaultSpecifier': checkDefault.bind(null, 'exported'), + ImportDefaultSpecifier: checkDefault.bind(null, 'local'), + ExportDefaultSpecifier: checkDefault.bind(null, 'exported'), }; }, }; diff --git a/src/rules/no-named-default.js b/src/rules/no-named-default.js index 8745ce3890..1ed0e31df5 100644 --- a/src/rules/no-named-default.js +++ b/src/rules/no-named-default.js @@ -13,7 +13,7 @@ module.exports = { create(context) { return { - 'ImportDeclaration': function (node) { + ImportDeclaration(node) { node.specifiers.forEach(function (im) { if (im.importKind === 'type' || im.importKind === 'typeof') { return; diff --git a/src/rules/no-named-export.js b/src/rules/no-named-export.js index b0722f3596..efaf9dc4c8 100644 --- a/src/rules/no-named-export.js +++ b/src/rules/no-named-export.js @@ -29,7 +29,7 @@ module.exports = { return context.report({ node, message }); } - const someNamed = node.specifiers.some(specifier => (specifier.exported.name || specifier.exported.value) !== 'default'); + const someNamed = node.specifiers.some((specifier) => (specifier.exported.name || specifier.exported.value) !== 'default'); if (someNamed) { context.report({ node, message }); } diff --git a/src/rules/no-namespace.js b/src/rules/no-namespace.js index a078137e65..d3e591876f 100644 --- a/src/rules/no-namespace.js +++ b/src/rules/no-namespace.js @@ -10,7 +10,6 @@ import docsUrl from '../docsUrl'; // Rule Definition //------------------------------------------------------------------------------ - module.exports = { meta: { type: 'suggestion', @@ -40,20 +39,20 @@ module.exports = { return { ImportNamespaceSpecifier(node) { - if (ignoreGlobs && ignoreGlobs.find(glob => minimatch(node.parent.source.value, glob, { matchBase: true }))) { + if (ignoreGlobs && ignoreGlobs.find((glob) => minimatch(node.parent.source.value, glob, { matchBase: true }))) { return; } const scopeVariables = context.getScope().variables; const namespaceVariable = scopeVariables.find((variable) => variable.defs[0].node === node); const namespaceReferences = namespaceVariable.references; - const namespaceIdentifiers = namespaceReferences.map(reference => reference.identifier); + const namespaceIdentifiers = namespaceReferences.map((reference) => reference.identifier); const canFix = namespaceIdentifiers.length > 0 && !usesNamespaceAsObject(namespaceIdentifiers); context.report({ node, message: `Unexpected namespace import.`, - fix: canFix && (fixer => { + fix: canFix && ((fixer) => { const scopeManager = context.getSourceCode().scopeManager; const fixes = []; @@ -82,11 +81,10 @@ module.exports = { ); // Replace the ImportNamespaceSpecifier with a list of ImportSpecifiers - const namedImportSpecifiers = importNames.map((importName) => ( - importName === importLocalNames[importName] - ? importName - : `${importName} as ${importLocalNames[importName]}` - )); + const namedImportSpecifiers = importNames.map((importName) => importName === importLocalNames[importName] + ? importName + : `${importName} as ${importLocalNames[importName]}`, + ); fixes.push(fixer.replaceText(node, `{ ${namedImportSpecifiers.join(', ')} }`)); // Pass 2: Replace references to the namespace with references to the named imports @@ -116,8 +114,9 @@ function usesNamespaceAsObject(namespaceIdentifiers) { // `namespace.x` or `namespace['x']` return ( - parent && parent.type === 'MemberExpression' && - (parent.property.type === 'Identifier' || parent.property.type === 'Literal') + parent + && parent.type === 'MemberExpression' + && (parent.property.type === 'Identifier' || parent.property.type === 'Literal') ); }); } @@ -144,7 +143,7 @@ function getVariableNamesInScope(scopeManager, node) { currentNode = currentNode.parent; scope = scopeManager.acquire(currentNode, true); } - return new Set(scope.variables.concat(scope.upper.variables).map(variable => variable.name)); + return new Set(scope.variables.concat(scope.upper.variables).map((variable) => variable.name)); } /** diff --git a/src/rules/no-nodejs-modules.js b/src/rules/no-nodejs-modules.js index a87bff796f..82594bb603 100644 --- a/src/rules/no-nodejs-modules.js +++ b/src/rules/no-nodejs-modules.js @@ -4,7 +4,7 @@ import docsUrl from '../docsUrl'; function reportIfMissing(context, node, allowed, name) { if (allowed.indexOf(name) === -1 && importType(name, context) === 'builtin') { - context.report(node, 'Do not import Node.js builtin module "' + name + '"'); + context.report(node, `Do not import Node.js builtin module "${name}"`); } } diff --git a/src/rules/no-relative-packages.js b/src/rules/no-relative-packages.js index 6b0a627670..1d215519fd 100644 --- a/src/rules/no-relative-packages.js +++ b/src/rules/no-relative-packages.js @@ -47,7 +47,7 @@ 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))) + fix: (fixer) => fixer.replaceText(node, JSON.stringify(toPosixPath(properImport))) , }); } diff --git a/src/rules/no-relative-parent-imports.js b/src/rules/no-relative-parent-imports.js index fd8dcb302f..decd2ef7d2 100644 --- a/src/rules/no-relative-parent-imports.js +++ b/src/rules/no-relative-parent-imports.js @@ -18,7 +18,7 @@ module.exports = { create: function noRelativePackages(context) { const myPath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); - if (myPath === '') return {}; // can't check a non-file + if (myPath === '') { return {}; } // can't check a non-file function checkSourceValue(sourceNode) { const depPath = sourceNode.value; @@ -38,10 +38,7 @@ module.exports = { if (importType(relDepPath, context) === 'parent') { context.report({ node: sourceNode, - message: 'Relative imports from parent directories are not allowed. ' + - `Please either pass what you're importing through at runtime ` + - `(dependency injection), move \`${basename(myPath)}\` to same ` + - `directory as \`${depPath}\` or consider making \`${depPath}\` a package.`, + message: `Relative imports from parent directories are not allowed. Please either pass what you're importing through at runtime (dependency injection), move \`${basename(myPath)}\` to same directory as \`${depPath}\` or consider making \`${depPath}\` a package.`, }); } } diff --git a/src/rules/no-restricted-paths.js b/src/rules/no-restricted-paths.js index 2293119592..bce9fd1a03 100644 --- a/src/rules/no-restricted-paths.js +++ b/src/rules/no-restricted-paths.js @@ -77,11 +77,9 @@ module.exports = { const restrictedPaths = options.zones || []; const basePath = options.basePath || process.cwd(); const currentFilename = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); - const matchingZones = restrictedPaths.filter((zone) => { - return [].concat(zone.target) - .map(target => path.resolve(basePath, target)) - .some(targetPath => isMatchingTargetPath(currentFilename, targetPath)); - }); + const matchingZones = restrictedPaths.filter((zone) => [].concat(zone.target) + .map((target) => path.resolve(basePath, target)) + .some((targetPath) => isMatchingTargetPath(currentFilename, targetPath))); function isMatchingTargetPath(filename, targetPath) { if (isGlob(targetPath)) { @@ -180,7 +178,7 @@ module.exports = { } function reportInvalidExceptions(validators, node) { - validators.forEach(validator => validator.reportInvalidException(node)); + validators.forEach((validator) => validator.reportInvalidException(node)); } function reportImportsInRestrictedZone(validators, node, importPath, customMessage) { @@ -203,7 +201,7 @@ module.exports = { const isGlobPattern = areGlobPatterns.every((isGlob) => isGlob); - return allZoneFrom.map(singleZoneFrom => { + return allZoneFrom.map((singleZoneFrom) => { const absoluteFrom = path.resolve(basePath, singleZoneFrom); if (isGlobPattern) { @@ -227,14 +225,14 @@ module.exports = { validators[index] = makePathValidators(zone.from, zone.except); } - const applicableValidatorsForImportPath = validators[index].filter(validator => validator.isPathRestricted(absoluteImportPath)); + const applicableValidatorsForImportPath = validators[index].filter((validator) => validator.isPathRestricted(absoluteImportPath)); - const validatorsWithInvalidExceptions = applicableValidatorsForImportPath.filter(validator => !validator.hasValidExceptions); + const validatorsWithInvalidExceptions = applicableValidatorsForImportPath.filter((validator) => !validator.hasValidExceptions); reportInvalidExceptions(validatorsWithInvalidExceptions, node); const applicableValidatorsForImportPathExcludingExceptions = applicableValidatorsForImportPath - .filter(validator => validator.hasValidExceptions) - .filter(validator => !validator.isPathException(absoluteImportPath)); + .filter((validator) => validator.hasValidExceptions) + .filter((validator) => !validator.isPathException(absoluteImportPath)); reportImportsInRestrictedZone(applicableValidatorsForImportPathExcludingExceptions, node, importPath, zone.message); }); } diff --git a/src/rules/no-unassigned-import.js b/src/rules/no-unassigned-import.js index b790141927..0af9f2e9f3 100644 --- a/src/rules/no-unassigned-import.js +++ b/src/rules/no-unassigned-import.js @@ -24,16 +24,15 @@ function testIsAllow(globs, filename, source) { filePath = path.resolve(path.dirname(filename), source); // get source absolute path } - return globs.find(glob => ( - minimatch(filePath, glob) || - minimatch(filePath, path.join(process.cwd(), glob)) - )) !== undefined; + return globs.find((glob) => minimatch(filePath, glob) + || minimatch(filePath, path.join(process.cwd(), glob)), + ) !== undefined; } function create(context) { const options = context.options[0] || {}; const filename = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); - const isAllow = source => testIsAllow(options.allow, filename, source); + const isAllow = (source) => testIsAllow(options.allow, filename, source); return { ImportDeclaration(node) { @@ -42,9 +41,11 @@ function create(context) { } }, ExpressionStatement(node) { - if (node.expression.type === 'CallExpression' && - isStaticRequire(node.expression) && - !isAllow(node.expression.arguments[0].value)) { + if ( + node.expression.type === 'CallExpression' + && isStaticRequire(node.expression) + && !isAllow(node.expression.arguments[0].value) + ) { report(context, node.expression); } }, @@ -62,19 +63,19 @@ module.exports = { }, schema: [ { - 'type': 'object', - 'properties': { - 'devDependencies': { 'type': ['boolean', 'array'] }, - 'optionalDependencies': { 'type': ['boolean', 'array'] }, - 'peerDependencies': { 'type': ['boolean', 'array'] }, - 'allow': { - 'type': 'array', - 'items': { - 'type': 'string', + type: 'object', + properties: { + devDependencies: { type: ['boolean', 'array'] }, + optionalDependencies: { type: ['boolean', 'array'] }, + peerDependencies: { type: ['boolean', 'array'] }, + allow: { + type: 'array', + items: { + type: 'string', }, }, }, - 'additionalProperties': false, + additionalProperties: false, }, ], }, diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index bd8c524abb..4b09128a10 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -40,11 +40,12 @@ try { const { listFilesToProcess: originalListFilesToProcess } = require('eslint/lib/util/glob-util'); listFilesToProcess = function (src, extensions) { - const patterns = src.reduce((carry, pattern) => { - return carry.concat(extensions.map((extension) => { - return /\*\*|\*\./.test(pattern) ? pattern : `${pattern}/**/*${extension}`; - })); - }, src.slice()); + const patterns = src.reduce( + (carry, pattern) => carry.concat( + extensions.map((extension) => (/\*\*|\*\./).test(pattern) ? pattern : `${pattern}/**/*${extension}`), + ), + src, + ); return originalListFilesToProcess(patterns); }; @@ -84,11 +85,11 @@ const DEFAULT = 'default'; function forEachDeclarationIdentifier(declaration, cb) { if (declaration) { if ( - declaration.type === FUNCTION_DECLARATION || - declaration.type === CLASS_DECLARATION || - declaration.type === TS_INTERFACE_DECLARATION || - declaration.type === TS_TYPE_ALIAS_DECLARATION || - declaration.type === TS_ENUM_DECLARATION + declaration.type === FUNCTION_DECLARATION + || declaration.type === CLASS_DECLARATION + || declaration.type === TS_INTERFACE_DECLARATION + || declaration.type === TS_TYPE_ALIAS_DECLARATION + || declaration.type === TS_ENUM_DECLARATION ) { cb(declaration.id.name); } else if (declaration.type === VARIABLE_DECLARATION) { @@ -160,9 +161,7 @@ const visitorKeyMap = new Map(); const ignoredFiles = new Set(); const filesOutsideSrc = new Set(); -const isNodeModule = path => { - return /\/(node_modules)\//.test(path); -}; +const isNodeModule = (path) => (/\/(node_modules)\//).test(path); /** * read all files matching the patterns in src and ignoreExports @@ -191,7 +190,7 @@ const resolveFiles = (src, ignoreExports, context) => { */ const prepareImportsAndExports = (srcFiles, context) => { const exportAll = new Map(); - srcFiles.forEach(file => { + srcFiles.forEach((file) => { const exports = new Map(); const imports = new Map(); const currentExports = Exports.get(file, context); @@ -207,7 +206,7 @@ const prepareImportsAndExports = (srcFiles, context) => { visitorKeyMap.set(file, visitorKeys); // dependencies === export * from const currentExportAll = new Set(); - dependencies.forEach(getDependency => { + dependencies.forEach((getDependency) => { const dependency = getDependency(); if (dependency === null) { return; @@ -247,9 +246,11 @@ const prepareImportsAndExports = (srcFiles, context) => { return; } const localImport = imports.get(key) || new Set(); - value.declarations.forEach(({ importedSpecifiers }) => - importedSpecifiers.forEach(specifier => localImport.add(specifier)), - ); + value.declarations.forEach(({ importedSpecifiers }) => { + importedSpecifiers.forEach((specifier) => { + localImport.add(specifier); + }); + }); imports.set(key, localImport); }); importList.set(file, imports); @@ -271,7 +272,7 @@ const prepareImportsAndExports = (srcFiles, context) => { exportList.set(file, exports); }); exportAll.forEach((value, key) => { - value.forEach(val => { + value.forEach((val) => { const currentExports = exportList.get(val); if (currentExports) { const currentExport = currentExports.get(EXPORT_ALL_DECLARATION); @@ -290,7 +291,7 @@ const determineUsage = () => { listValue.forEach((value, key) => { const exports = exportList.get(key); if (typeof exports !== 'undefined') { - value.forEach(currentImport => { + value.forEach((currentImport) => { let specifier; if (currentImport === IMPORT_NAMESPACE_SPECIFIER) { specifier = IMPORT_NAMESPACE_SPECIFIER; @@ -313,7 +314,7 @@ const determineUsage = () => { }); }; -const getSrc = src => { +const getSrc = (src) => { if (src) { return src; } @@ -347,33 +348,31 @@ const doPreparation = (src, ignoreExports, context) => { lastPrepareKey = prepareKey; }; -const newNamespaceImportExists = specifiers => - specifiers.some(({ type }) => type === IMPORT_NAMESPACE_SPECIFIER); +const newNamespaceImportExists = (specifiers) => specifiers.some(({ type }) => type === IMPORT_NAMESPACE_SPECIFIER); -const newDefaultImportExists = specifiers => - specifiers.some(({ type }) => type === IMPORT_DEFAULT_SPECIFIER); +const newDefaultImportExists = (specifiers) => specifiers.some(({ type }) => type === IMPORT_DEFAULT_SPECIFIER); -const fileIsInPkg = file => { +const fileIsInPkg = (file) => { const { path, pkg } = readPkgUp({ cwd: file }); const basePath = dirname(path); - const checkPkgFieldString = pkgField => { + const checkPkgFieldString = (pkgField) => { if (join(basePath, pkgField) === file) { return true; } }; - const checkPkgFieldObject = pkgField => { + const checkPkgFieldObject = (pkgField) => { const pkgFieldFiles = values(pkgField) .filter((value) => typeof value !== 'boolean') - .map(value => join(basePath, value)); + .map((value) => join(basePath, value)); if (includes(pkgFieldFiles, file)) { return true; } }; - const checkPkgField = pkgField => { + const checkPkgField = (pkgField) => { if (typeof pkgField === 'string') { return checkPkgFieldString(pkgField); } @@ -452,7 +451,7 @@ module.exports = { missingExports: { enum: [false] }, }, }, - anyOf:[{ + anyOf: [{ not: { properties: { unusedExports: { enum: [true] }, @@ -480,7 +479,7 @@ module.exports = { }], }, - create: context => { + create(context) { const { src, ignoreExports = [], @@ -494,7 +493,7 @@ module.exports = { const file = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); - const checkExportPresence = node => { + const checkExportPresence = (node) => { if (!missingExports) { return; } @@ -589,7 +588,7 @@ module.exports = { * * update lists of existing exports during runtime */ - const updateExportUsage = node => { + const updateExportUsage = (node) => { if (ignoredFiles.has(file)) { return; } @@ -611,7 +610,7 @@ module.exports = { } if (type === EXPORT_NAMED_DECLARATION) { if (specifiers.length > 0) { - specifiers.forEach(specifier => { + specifiers.forEach((specifier) => { if (specifier.exported) { newExportIdentifiers.add(specifier.exported.name || specifier.exported.value); } @@ -631,7 +630,7 @@ module.exports = { }); // new export identifiers added: add to map of new exports - newExportIdentifiers.forEach(key => { + newExportIdentifiers.forEach((key) => { if (!exports.has(key)) { newExports.set(key, { whereUsed: new Set() }); } @@ -655,7 +654,7 @@ module.exports = { * * update lists of existing imports during runtime */ - const updateImportUsage = node => { + const updateImportUsage = (node) => { if (!unusedExports) { return; } @@ -686,9 +685,11 @@ module.exports = { if (value.has(IMPORT_DEFAULT_SPECIFIER)) { oldDefaultImports.add(key); } - value.forEach(val => { - if (val !== IMPORT_NAMESPACE_SPECIFIER && - val !== IMPORT_DEFAULT_SPECIFIER) { + value.forEach((val) => { + if ( + val !== IMPORT_NAMESPACE_SPECIFIER + && val !== IMPORT_DEFAULT_SPECIFIER + ) { oldImports.set(val, key); } }); @@ -716,14 +717,14 @@ module.exports = { }, }); - node.body.forEach(astNode => { + node.body.forEach((astNode) => { let resolvedPath; // support for export { value } from 'module' if (astNode.type === EXPORT_NAMED_DECLARATION) { if (astNode.source) { resolvedPath = resolve(astNode.source.raw.replace(/('|")/g, ''), context); - astNode.specifiers.forEach(specifier => { + astNode.specifiers.forEach((specifier) => { const name = specifier.local.name || specifier.local.value; if (name === DEFAULT) { newDefaultImports.add(resolvedPath); @@ -757,17 +758,15 @@ module.exports = { newDefaultImports.add(resolvedPath); } - astNode.specifiers.forEach(specifier => { - if (specifier.type === IMPORT_DEFAULT_SPECIFIER || - specifier.type === IMPORT_NAMESPACE_SPECIFIER) { - return; - } - newImports.set(specifier.imported.name || specifier.imported.value, resolvedPath); - }); + astNode.specifiers + .filter((specifier) => specifier.type !== IMPORT_DEFAULT_SPECIFIER && specifier.type !== IMPORT_NAMESPACE_SPECIFIER) + .forEach((specifier) => { + newImports.set(specifier.imported.name || specifier.imported.value, resolvedPath); + }); } }); - newExportAll.forEach(value => { + newExportAll.forEach((value) => { if (!oldExportAll.has(value)) { let imports = oldImportPaths.get(value); if (typeof imports === 'undefined') { @@ -795,7 +794,7 @@ module.exports = { } }); - oldExportAll.forEach(value => { + oldExportAll.forEach((value) => { if (!newExportAll.has(value)) { const imports = oldImportPaths.get(value); imports.delete(EXPORT_ALL_DECLARATION); @@ -810,7 +809,7 @@ module.exports = { } }); - newDefaultImports.forEach(value => { + newDefaultImports.forEach((value) => { if (!oldDefaultImports.has(value)) { let imports = oldImportPaths.get(value); if (typeof imports === 'undefined') { @@ -838,7 +837,7 @@ module.exports = { } }); - oldDefaultImports.forEach(value => { + oldDefaultImports.forEach((value) => { if (!newDefaultImports.has(value)) { const imports = oldImportPaths.get(value); imports.delete(IMPORT_DEFAULT_SPECIFIER); @@ -853,7 +852,7 @@ module.exports = { } }); - newNamespaceImports.forEach(value => { + newNamespaceImports.forEach((value) => { if (!oldNamespaceImports.has(value)) { let imports = oldImportPaths.get(value); if (typeof imports === 'undefined') { @@ -881,7 +880,7 @@ module.exports = { } }); - oldNamespaceImports.forEach(value => { + oldNamespaceImports.forEach((value) => { if (!newNamespaceImports.has(value)) { const imports = oldImportPaths.get(value); imports.delete(IMPORT_NAMESPACE_SPECIFIER); @@ -941,16 +940,16 @@ module.exports = { }; return { - 'Program:exit': node => { + 'Program:exit'(node) { updateExportUsage(node); updateImportUsage(node); checkExportPresence(node); }, - 'ExportDefaultDeclaration': node => { + ExportDefaultDeclaration(node) { checkUsage(node, IMPORT_DEFAULT_SPECIFIER); }, - 'ExportNamedDeclaration': node => { - node.specifiers.forEach(specifier => { + ExportNamedDeclaration(node) { + node.specifiers.forEach((specifier) => { checkUsage(node, specifier.exported.name || specifier.exported.value); }); forEachDeclarationIdentifier(node.declaration, (name) => { diff --git a/src/rules/no-useless-path-segments.js b/src/rules/no-useless-path-segments.js index a328be2465..343a4f6230 100644 --- a/src/rules/no-useless-path-segments.js +++ b/src/rules/no-useless-path-segments.js @@ -25,7 +25,7 @@ import docsUrl from '../docsUrl'; function toRelativePath(relativePath) { const stripped = relativePath.replace(/\/$/g, ''); // Remove trailing / - return /^((\.\.)|(\.))($|\/)/.test(stripped) ? stripped : `./${stripped}`; + return (/^((\.\.)|(\.))($|\/)/).test(stripped) ? stripped : `./${stripped}`; } function normalize(fn) { @@ -71,7 +71,7 @@ module.exports = { node: source, // Note: Using messageIds is not possible due to the support for ESLint 2 and 3 message: `Useless path segments for "${importPath}", should be "${proposedPath}"`, - fix: fixer => proposedPath && fixer.replaceText(source, JSON.stringify(proposedPath)), + fix: (fixer) => proposedPath && fixer.replaceText(source, JSON.stringify(proposedPath)), }); } diff --git a/src/rules/no-webpack-loader-syntax.js b/src/rules/no-webpack-loader-syntax.js index faedeb4373..6ca7d603d6 100644 --- a/src/rules/no-webpack-loader-syntax.js +++ b/src/rules/no-webpack-loader-syntax.js @@ -3,9 +3,7 @@ import docsUrl from '../docsUrl'; function reportIfNonStandard(context, node, name) { if (name && name.indexOf('!') !== -1) { - context.report(node, `Unexpected '!' in '${name}'. ` + - 'Do not use import syntax to configure webpack loaders.', - ); + context.report(node, `Unexpected '!' in '${name}'. Do not use import syntax to configure webpack loaders.`); } } diff --git a/src/rules/order.js b/src/rules/order.js index bdead9d40c..5921989d2b 100644 --- a/src/rules/order.js +++ b/src/rules/order.js @@ -13,7 +13,7 @@ const defaultGroups = ['builtin', 'external', 'parent', 'sibling', 'index']; function reverse(array) { return array.map(function (v) { - return Object.assign({}, v, { rank: -v.rank }); + return { ...v, rank: -v.rank }; }).reverse(); } @@ -111,9 +111,9 @@ function findEndOfLineWithComments(sourceCode, node) { } function commentOnSameLineAs(node) { - return token => (token.type === 'Block' || token.type === 'Line') && - token.loc.start.line === token.loc.end.line && - token.loc.end.line === node.loc.end.line; + return (token) => (token.type === 'Block' || token.type === 'Line') + && token.loc.start.line === token.loc.end.line + && token.loc.end.line === node.loc.end.line; } function findStartOfLineWithComments(sourceCode, node) { @@ -130,13 +130,13 @@ function findStartOfLineWithComments(sourceCode, node) { } function isRequireExpression(expr) { - return expr != null && - expr.type === 'CallExpression' && - expr.callee != null && - expr.callee.name === 'require' && - expr.arguments != null && - expr.arguments.length === 1 && - expr.arguments[0].type === 'Literal'; + return expr != null + && expr.type === 'CallExpression' + && expr.callee != null + && expr.callee.name === 'require' + && expr.arguments != null + && expr.arguments.length === 1 + && expr.arguments[0].type === 'Literal'; } function isSupportedRequireModule(node) { @@ -147,16 +147,16 @@ function isSupportedRequireModule(node) { return false; } const decl = node.declarations[0]; - const isPlainRequire = decl.id && - (decl.id.type === 'Identifier' || decl.id.type === 'ObjectPattern') && - isRequireExpression(decl.init); - const isRequireWithMemberExpression = decl.id && - (decl.id.type === 'Identifier' || decl.id.type === 'ObjectPattern') && - decl.init != null && - decl.init.type === 'CallExpression' && - decl.init.callee != null && - decl.init.callee.type === 'MemberExpression' && - isRequireExpression(decl.init.callee.object); + const isPlainRequire = decl.id + && (decl.id.type === 'Identifier' || decl.id.type === 'ObjectPattern') + && isRequireExpression(decl.init); + const isRequireWithMemberExpression = decl.id + && (decl.id.type === 'Identifier' || decl.id.type === 'ObjectPattern') + && decl.init != null + && decl.init.type === 'CallExpression' + && decl.init.callee != null + && decl.init.callee.type === 'MemberExpression' + && isRequireExpression(decl.init.callee.object); return isPlainRequire || isRequireWithMemberExpression; } @@ -211,7 +211,7 @@ function fixOutOfOrder(context, firstNode, secondNode, order) { let newCode = sourceCode.text.substring(secondRootStart, secondRootEnd); if (newCode[newCode.length - 1] !== '\n') { - newCode = newCode + '\n'; + newCode = `${newCode}\n`; } const firstImport = `${makeImportDescription(firstNode)} of \`${firstNode.displayName}\``; @@ -222,21 +222,19 @@ function fixOutOfOrder(context, firstNode, secondNode, order) { context.report({ node: secondNode.node, message, - fix: canFix && (fixer => - fixer.replaceTextRange( - [firstRootStart, secondRootEnd], - newCode + sourceCode.text.substring(firstRootStart, secondRootStart), - )), + fix: canFix && ((fixer) => fixer.replaceTextRange( + [firstRootStart, secondRootEnd], + newCode + sourceCode.text.substring(firstRootStart, secondRootStart), + )), }); } else if (order === 'after') { context.report({ node: secondNode.node, message, - fix: canFix && (fixer => - fixer.replaceTextRange( - [secondRootStart, firstRootEnd], - sourceCode.text.substring(secondRootEnd, firstRootEnd) + newCode, - )), + fix: canFix && ((fixer) => fixer.replaceTextRange( + [secondRootStart, firstRootEnd], + sourceCode.text.substring(secondRootEnd, firstRootEnd) + newCode, + )), }); } } @@ -285,8 +283,8 @@ const getNormalizedValue = (node, toLowerCase) => { function getSorter(alphabetizeOptions) { const multiplier = alphabetizeOptions.order === 'asc' ? 1 : -1; const orderImportKind = alphabetizeOptions.orderImportKind; - const multiplierImportKind = orderImportKind !== 'ignore' && - (alphabetizeOptions.orderImportKind === 'asc' ? 1 : -1); + const multiplierImportKind = orderImportKind !== 'ignore' + && (alphabetizeOptions.orderImportKind === 'asc' ? 1 : -1); return function importsSorter(nodeA, nodeB) { const importA = getNormalizedValue(nodeA, alphabetizeOptions.caseInsensitive); @@ -303,7 +301,7 @@ function getSorter(alphabetizeOptions) { for (let i = 0; i < Math.min(a, b); i++) { result = compareString(A[i], B[i]); - if (result) break; + if (result) { break; } } if (!result && a !== b) { @@ -368,7 +366,7 @@ function computePathRank(ranks, pathGroups, path, maxPosition) { for (let i = 0, l = pathGroups.length; i < l; i++) { const { pattern, patternOptions, group, position = 1 } = pathGroups[i]; if (minimatch(path, pattern, patternOptions || { nocomment: true })) { - return ranks[group] + (position / maxPosition); + return ranks[group] + position / maxPosition; } } } @@ -399,7 +397,7 @@ function computeRank(context, ranks, importEntry, excludedImportTypes) { function registerNode(context, importEntry, ranks, imported, excludedImportTypes) { const rank = computeRank(context, ranks, importEntry, excludedImportTypes); if (rank !== -1) { - imported.push(Object.assign({}, importEntry, { rank })); + imported.push({ ...importEntry, rank }); } } @@ -408,15 +406,15 @@ function getRequireBlock(node) { // Handle cases like `const baz = require('foo').bar.baz` // and `const foo = require('foo')()` while ( - (n.parent.type === 'MemberExpression' && n.parent.object === n) || - (n.parent.type === 'CallExpression' && n.parent.callee === n) + n.parent.type === 'MemberExpression' && n.parent.object === n + || n.parent.type === 'CallExpression' && n.parent.callee === n ) { n = n.parent; } if ( - n.parent.type === 'VariableDeclarator' && - n.parent.parent.type === 'VariableDeclaration' && - n.parent.parent.parent.type === 'Program' + n.parent.type === 'VariableDeclarator' + && n.parent.parent.type === 'VariableDeclaration' + && n.parent.parent.parent.type === 'Program' ) { return n.parent.parent.parent; } @@ -434,11 +432,10 @@ function convertGroupsToRanks(groups) { } group.forEach(function (groupItem) { if (types.indexOf(groupItem) === -1) { - throw new Error('Incorrect configuration of the rule: Unknown type `' + - JSON.stringify(groupItem) + '`'); + throw new Error(`Incorrect configuration of the rule: Unknown type \`${JSON.stringify(groupItem)}\``); } if (res[groupItem] !== undefined) { - throw new Error('Incorrect configuration of the rule: `' + groupItem + '` is duplicated'); + throw new Error(`Incorrect configuration of the rule: \`${groupItem}\` is duplicated`); } res[groupItem] = index * 2; }); @@ -476,7 +473,7 @@ function convertPathGroupsForRanks(pathGroups) { before[group].push(index); } - return Object.assign({}, pathGroup, { position }); + return { ...pathGroup, position }; }); let maxPosition = 1; @@ -520,7 +517,7 @@ function removeNewLineAfterImport(context, currentImport, previousImport) { findEndOfLineWithComments(sourceCode, prevRoot), findStartOfLineWithComments(sourceCode, currRoot), ]; - if (/^\s*$/.test(sourceCode.text.substring(rangeToRemove[0], rangeToRemove[1]))) { + if ((/^\s*$/).test(sourceCode.text.substring(rangeToRemove[0], rangeToRemove[1]))) { return (fixer) => fixer.removeRange(rangeToRemove); } return undefined; @@ -535,9 +532,7 @@ function makeNewlinesBetweenReport(context, imported, newlinesBetweenImports, di return linesBetweenImports.filter((line) => !line.trim().length).length; }; - const getIsStartOfDistinctGroup = (currentImport, previousImport) => { - return currentImport.rank - 1 >= previousImport.rank; - }; + const getIsStartOfDistinctGroup = (currentImport, previousImport) => currentImport.rank - 1 >= previousImport.rank; let previousImport = imported[0]; imported.slice(1).forEach(function (currentImport) { @@ -547,7 +542,7 @@ function makeNewlinesBetweenReport(context, imported, newlinesBetweenImports, di if (newlinesBetweenImports === 'always' || newlinesBetweenImports === 'always-and-inside-groups') { if (currentImport.rank !== previousImport.rank && emptyLinesBetween === 0) { - if (distinctGroup || (!distinctGroup && isStartOfDistinctGroup)) { + if (distinctGroup || !distinctGroup && isStartOfDistinctGroup) { context.report({ node: previousImport.node, message: 'There should be at least one empty line between import groups', @@ -556,7 +551,7 @@ function makeNewlinesBetweenReport(context, imported, newlinesBetweenImports, di } } else if (emptyLinesBetween > 0 && newlinesBetweenImports !== 'always-and-inside-groups') { - if ((distinctGroup && currentImport.rank === previousImport.rank) || (!distinctGroup && !isStartOfDistinctGroup)) { + if (distinctGroup && currentImport.rank === previousImport.rank || !distinctGroup && !isStartOfDistinctGroup) { context.report({ node: previousImport.node, message: 'There should be no empty line within import group', @@ -675,7 +670,7 @@ module.exports = { create: function importOrderRule(context) { const options = context.options[0] || {}; const newlinesBetweenImports = options['newlines-between'] || 'ignore'; - const pathGroupsExcludedImportTypes = new Set(options['pathGroupsExcludedImportTypes'] || ['builtin', 'external', 'object']); + const pathGroupsExcludedImportTypes = new Set(options.pathGroupsExcludedImportTypes || ['builtin', 'external', 'object']); const alphabetize = getAlphabetizeConfig(options); const distinctGroup = options.distinctGroup == null ? defaultDistinctGroup : !!options.distinctGroup; let ranks; diff --git a/src/rules/prefer-default-export.js b/src/rules/prefer-default-export.js index 32ef5004fa..581f02502e 100644 --- a/src/rules/prefer-default-export.js +++ b/src/rules/prefer-default-export.js @@ -15,7 +15,7 @@ module.exports = { }, schema: [{ type: 'object', - properties:{ + properties: { target: { type: 'string', enum: ['single', 'any'], @@ -51,11 +51,11 @@ module.exports = { } return { - 'ExportDefaultSpecifier': function () { + ExportDefaultSpecifier() { hasDefaultExport = true; }, - 'ExportSpecifier': function (node) { + ExportSpecifier(node) { if ((node.exported.name || node.exported.value) === 'default') { hasDefaultExport = true; } else { @@ -64,17 +64,17 @@ module.exports = { } }, - 'ExportNamedDeclaration': function (node) { + ExportNamedDeclaration(node) { // if there are specifiers, node.declaration should be null - if (!node.declaration) return; + if (!node.declaration) { return; } const { type } = node.declaration; if ( - type === 'TSTypeAliasDeclaration' || - type === 'TypeAlias' || - type === 'TSInterfaceDeclaration' || - type === 'InterfaceDeclaration' + type === 'TSTypeAliasDeclaration' + || type === 'TypeAlias' + || type === 'TSInterfaceDeclaration' + || type === 'InterfaceDeclaration' ) { specifierExportCount++; hasTypeExport = true; @@ -93,15 +93,15 @@ module.exports = { namedExportNode = node; }, - 'ExportDefaultDeclaration': function () { + ExportDefaultDeclaration() { hasDefaultExport = true; }, - 'ExportAllDeclaration': function () { + ExportAllDeclaration() { hasStarExport = true; }, - 'Program:exit': function () { + 'Program:exit'() { if (hasDefaultExport || hasStarExport || hasTypeExport) { return; } diff --git a/tests/src/cli.js b/tests/src/cli.js index e6afd8e441..8a73454878 100644 --- a/tests/src/cli.js +++ b/tests/src/cli.js @@ -21,7 +21,7 @@ describe('CLI regression tests', function () { rulePaths: ['./src/rules'], overrideConfig: { rules: { - 'named': 2, + named: 2, }, }, plugins: { 'eslint-plugin-import': importPlugin }, @@ -32,7 +32,7 @@ describe('CLI regression tests', function () { configFile: './tests/files/issue210.config.js', rulePaths: ['./src/rules'], rules: { - 'named': 2, + named: 2, }, }); cli.addPlugin('eslint-plugin-import', importPlugin); @@ -78,7 +78,7 @@ describe('CLI regression tests', function () { it('throws an error on invalid JSON', () => { const invalidJSON = './tests/files/just-json-files/invalid.json'; if (eslint) { - return eslint.lintFiles([invalidJSON]).then(results => { + return eslint.lintFiles([invalidJSON]).then((results) => { expect(results).to.eql( [ { @@ -97,16 +97,16 @@ describe('CLI regression tests', function () { }, ], errorCount: 1, - ...(semver.satisfies(eslintPkg.version, '>= 7.32 || ^8.0.0') && { + ...semver.satisfies(eslintPkg.version, '>= 7.32 || ^8.0.0') && { fatalErrorCount: 0, - }), + }, warningCount: 0, fixableErrorCount: 0, fixableWarningCount: 0, source: results[0].source, // NewLine-characters might differ depending on git-settings - ...(semver.satisfies(eslintPkg.version, '>= 8.8') && { + ...semver.satisfies(eslintPkg.version, '>= 8.8') && { suppressedMessages: [], - }), + }, usedDeprecatedRules: results[0].usedDeprecatedRules, // we don't care about this one }, ], diff --git a/tests/src/core/getExports.js b/tests/src/core/getExports.js index 86c1915968..1dd6e88014 100644 --- a/tests/src/core/getExports.js +++ b/tests/src/core/getExports.js @@ -58,11 +58,10 @@ describe('ExportMap', function () { const firstAccess = ExportMap.get('./named-exports', fakeContext); expect(firstAccess).to.exist; - const differentSettings = Object.assign( - {}, - fakeContext, - { parserPath: 'espree' }, - ); + const differentSettings = { + ...fakeContext, + parserPath: 'espree', + }; expect(ExportMap.get('./named-exports', differentSettings)) .to.exist.and @@ -320,17 +319,19 @@ describe('ExportMap', function () { }); it(`'has' circular reference`, function () { expect(ExportMap.get('./narcissist', fakeContext)) - .to.exist.and.satisfy(m => m.has('soGreat')); + .to.exist.and.satisfy((m) => m.has('soGreat')); }); it(`can 'get' circular reference`, function () { expect(ExportMap.get('./narcissist', fakeContext)) - .to.exist.and.satisfy(m => m.get('soGreat') != null); + .to.exist.and.satisfy((m) => m.get('soGreat') != null); }); }); context('issue #478: never parse non-whitelist extensions', function () { - const context = Object.assign({}, fakeContext, - { settings: { 'import/extensions': ['.js'] } }); + const context = { + ...fakeContext, + settings: { 'import/extensions': ['.js'] }, + }; let imports; before('load imports', function () { @@ -359,11 +360,13 @@ describe('ExportMap', function () { configs.forEach(([description, parserConfig]) => { describe(description, function () { - const context = Object.assign({}, fakeContext, - { settings: { + const context = { + ...fakeContext, + settings: { 'import/extensions': ['.js'], 'import/parsers': parserConfig, - } }); + }, + }; let imports; before('load imports', function () { @@ -404,30 +407,24 @@ describe('ExportMap', function () { }); it('should cache tsconfig until tsconfigRootDir parser option changes', function () { - const customContext = Object.assign( - {}, - context, - { - parserOptions: { - tsconfigRootDir: null, - }, + const customContext = { + ...context, + parserOptions: { + tsconfigRootDir: null, }, - ); + }; expect(tsConfigLoader.tsConfigLoader.callCount).to.equal(0); ExportMap.parse('./baz.ts', 'export const baz = 5', customContext); expect(tsConfigLoader.tsConfigLoader.callCount).to.equal(1); ExportMap.parse('./baz.ts', 'export const baz = 5', customContext); expect(tsConfigLoader.tsConfigLoader.callCount).to.equal(1); - const differentContext = Object.assign( - {}, - context, - { - parserOptions: { - tsconfigRootDir: process.cwd(), - }, + const differentContext = { + ...context, + parserOptions: { + tsconfigRootDir: process.cwd(), }, - ); + }; ExportMap.parse('./baz.ts', 'export const baz = 5', differentContext); expect(tsConfigLoader.tsConfigLoader.callCount).to.equal(2); @@ -460,7 +457,7 @@ describe('ExportMap', function () { for (const [testFile, expectedRegexResult] of testFiles) { it(`works for ${testFile} (${expectedRegexResult})`, function () { - const content = fs.readFileSync('./tests/files/' + testFile, 'utf8'); + const content = fs.readFileSync(`./tests/files/${testFile}`, 'utf8'); expect(testUnambiguous(content)).to.equal(expectedRegexResult); }); } diff --git a/tests/src/core/hash.js b/tests/src/core/hash.js index e75783fb06..1d6a9eb85c 100644 --- a/tests/src/core/hash.js +++ b/tests/src/core/hash.js @@ -7,7 +7,7 @@ const createHash = require('crypto').createHash; function expectHash(actualHash, expectedString) { const expectedHash = createHash('sha256'); expectedHash.update(expectedString); - expect(actualHash.digest('hex'), 'to be a hex digest of sha256 hash of string <' + expectedString + '>').to.equal(expectedHash.digest('hex')); + expect(actualHash.digest('hex'), `to be a hex digest of sha256 hash of string <${expectedString}>`).to.equal(expectedHash.digest('hex')); } describe('hash', function () { diff --git a/tests/src/core/parse.js b/tests/src/core/parse.js index 4ab8370ed2..9c59453602 100644 --- a/tests/src/core/parse.js +++ b/tests/src/core/parse.js @@ -13,9 +13,19 @@ describe('parse(content, { settings, ecmaFeatures })', function () { const eslintParserPath = require.resolve('./eslintParser'); let content; - before((done) => - fs.readFile(path, { encoding: 'utf8' }, - (err, f) => { if (err) { done(err); } else { content = f; done(); }})); + before((done) => { + fs.readFile( + path, + { encoding: 'utf8' }, + (err, f) => { + if (err) { + done(err); + } else { + content = f; done(); + } + }, + ); + }); it('doesn\'t support JSX by default', function () { expect(() => parse(path, content, { parserPath: 'espree' })).to.throw(Error); diff --git a/tests/src/core/resolve.js b/tests/src/core/resolve.js index 05a6aaeb68..6b69fb7f12 100644 --- a/tests/src/core/resolve.js +++ b/tests/src/core/resolve.js @@ -21,32 +21,38 @@ describe('resolve', function () { it('resolves via a custom resolver with interface version 1', function () { const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-v1' }); - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); - expect(resolve( '../files/exception' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('exception.js'); } }), + expect(resolve( + '../files/exception', + { ...testContext, getFilename() { return utils.getFilename('exception.js'); } }, )).to.equal(undefined); - expect(resolve( '../files/not-found' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('not-found.js'); } }), + expect(resolve( + '../files/not-found', + { ...testContext, getFilename() { return utils.getFilename('not-found.js'); } }, )).to.equal(undefined); }); it('resolves via a custom resolver with interface version 1 assumed if not specified', function () { const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-no-version' }); - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); - expect(resolve( '../files/exception' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('exception.js'); } }), + expect(resolve( + '../files/exception', + { ...testContext, getFilename() { return utils.getFilename('exception.js'); } }, )).to.equal(undefined); - expect(resolve( '../files/not-found' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('not-found.js'); } }), + expect(resolve( + '../files/not-found', + { ...testContext, getFilename() { return utils.getFilename('not-found.js'); } }, )).to.equal(undefined); }); @@ -57,21 +63,24 @@ describe('resolve', function () { testContextReports.push(reportInfo); }; - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); testContextReports.length = 0; - expect(resolve( '../files/exception' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('exception.js'); } }), + expect(resolve( + '../files/exception', + { ...testContext, getFilename() { return utils.getFilename('exception.js'); } }, )).to.equal(undefined); expect(testContextReports[0]).to.be.an('object'); expect(replaceErrorStackForTest(testContextReports[0].message)).to.equal('Resolve error: foo-bar-resolver-v2 resolve test exception\n'); expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 }); testContextReports.length = 0; - expect(resolve( '../files/not-found' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('not-found.js'); } }), + expect(resolve( + '../files/not-found', + { ...testContext, getFilename() { return utils.getFilename('not-found.js'); } }, )).to.equal(undefined); expect(testContextReports.length).to.equal(0); }); @@ -79,32 +88,36 @@ describe('resolve', function () { it('respects import/resolver as array of strings', function () { const testContext = utils.testContext({ 'import/resolver': [ './foo-bar-resolver-v2', './foo-bar-resolver-v1' ] }); - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); }); it('respects import/resolver as object', function () { const testContext = utils.testContext({ 'import/resolver': { './foo-bar-resolver-v2': {} } }); - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); }); it('respects import/resolver as array of objects', function () { const testContext = utils.testContext({ 'import/resolver': [ { './foo-bar-resolver-v2': {} }, { './foo-bar-resolver-v1': {} } ] }); - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); }); it('finds resolvers from the source files rather than eslint-module-utils', function () { - const testContext = utils.testContext({ 'import/resolver': { 'foo': {} } }); + const testContext = utils.testContext({ 'import/resolver': { foo: {} } }); - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); }); @@ -116,8 +129,9 @@ describe('resolve', function () { }; testContextReports.length = 0; - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(undefined); expect(testContextReports[0]).to.be.an('object'); expect(testContextReports[0].message).to.equal('Resolve error: invalid resolver config'); @@ -132,8 +146,9 @@ describe('resolve', function () { testContextReports.push(reportInfo); }; testContextReports.length = 0; - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(undefined); expect(testContextReports[0]).to.be.an('object'); expect(testContextReports[0].message).to.equal(`Resolve error: ${resolverName} with invalid interface loaded as resolver`); @@ -141,10 +156,11 @@ describe('resolve', function () { }); it('respects import/resolve extensions', function () { - const testContext = utils.testContext({ 'import/resolve': { 'extensions': ['.jsx'] } }); + const testContext = utils.testContext({ 'import/resolve': { extensions: ['.jsx'] } }); - expect(resolve( './jsx/MyCoolComponent' - , testContext, + expect(resolve( + './jsx/MyCoolComponent', + testContext, )).to.equal(utils.testFilePath('./jsx/MyCoolComponent.jsx')); }); @@ -155,8 +171,9 @@ describe('resolve', function () { testContextReports.push(reportInfo); }; - expect(resolve( '../files/exception' - , Object.assign({}, testContext, { getFilename() { return utils.getFilename('exception.js'); } }), + expect(resolve( + '../files/exception', + { ...testContext, getFilename() { return utils.getFilename('exception.js'); } }, )).to.equal(undefined); expect(testContextReports[0]).to.be.an('object'); expect(replaceErrorStackForTest(testContextReports[0].message)).to.equal('Resolve error: SyntaxError: TEST SYNTAX ERROR\n'); @@ -172,32 +189,38 @@ describe('resolve', function () { it('resolves via a custom resolver with interface version 1', function () { const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-v1' }); - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); - expect(resolve( '../files/exception' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }), + expect(resolve( + '../files/exception', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }, )).to.equal(undefined); - expect(resolve( '../files/not-found' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('not-found.js'); } }), + expect(resolve( + '../files/not-found', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('not-found.js'); } }, )).to.equal(undefined); }); it('resolves via a custom resolver with interface version 1 assumed if not specified', function () { const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-no-version' }); - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); - expect(resolve( '../files/exception' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }), + expect(resolve( + '../files/exception', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }, )).to.equal(undefined); - expect(resolve( '../files/not-found' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('not-found.js'); } }), + expect(resolve( + '../files/not-found', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('not-found.js'); } }, )).to.equal(undefined); }); @@ -208,21 +231,24 @@ describe('resolve', function () { testContextReports.push(reportInfo); }; - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); testContextReports.length = 0; - expect(resolve( '../files/exception' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }), + expect(resolve( + '../files/exception', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }, )).to.equal(undefined); expect(testContextReports[0]).to.be.an('object'); expect(replaceErrorStackForTest(testContextReports[0].message)).to.equal('Resolve error: foo-bar-resolver-v2 resolve test exception\n'); expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 }); testContextReports.length = 0; - expect(resolve( '../files/not-found' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('not-found.js'); } }), + expect(resolve( + '../files/not-found', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('not-found.js'); } }, )).to.equal(undefined); expect(testContextReports.length).to.equal(0); }); @@ -230,32 +256,36 @@ describe('resolve', function () { it('respects import/resolver as array of strings', function () { const testContext = utils.testContext({ 'import/resolver': [ './foo-bar-resolver-v2', './foo-bar-resolver-v1' ] }); - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); }); it('respects import/resolver as object', function () { const testContext = utils.testContext({ 'import/resolver': { './foo-bar-resolver-v2': {} } }); - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); }); it('respects import/resolver as array of objects', function () { const testContext = utils.testContext({ 'import/resolver': [ { './foo-bar-resolver-v2': {} }, { './foo-bar-resolver-v1': {} } ] }); - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); }); it('finds resolvers from the source files rather than eslint-module-utils', function () { - const testContext = utils.testContext({ 'import/resolver': { 'foo': {} } }); + const testContext = utils.testContext({ 'import/resolver': { foo: {} } }); - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(utils.testFilePath('./bar.jsx')); }); @@ -267,8 +297,9 @@ describe('resolve', function () { }; testContextReports.length = 0; - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(undefined); expect(testContextReports[0]).to.be.an('object'); expect(testContextReports[0].message).to.equal('Resolve error: invalid resolver config'); @@ -283,8 +314,9 @@ describe('resolve', function () { testContextReports.push(reportInfo); }; testContextReports.length = 0; - expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }), + expect(resolve( + '../files/foo', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }, )).to.equal(undefined); expect(testContextReports[0]).to.be.an('object'); expect(testContextReports[0].message).to.equal(`Resolve error: ${resolverName} with invalid interface loaded as resolver`); @@ -292,10 +324,11 @@ describe('resolve', function () { }); it('respects import/resolve extensions', function () { - const testContext = utils.testContext({ 'import/resolve': { 'extensions': ['.jsx'] } }); + const testContext = utils.testContext({ 'import/resolve': { extensions: ['.jsx'] } }); - expect(resolve( './jsx/MyCoolComponent' - , testContext, + expect(resolve( + './jsx/MyCoolComponent', + testContext, )).to.equal(utils.testFilePath('./jsx/MyCoolComponent.jsx')); }); @@ -306,8 +339,9 @@ describe('resolve', function () { testContextReports.push(reportInfo); }; - expect(resolve( '../files/exception' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }), + expect(resolve( + '../files/exception', + { ...testContext, getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }, )).to.equal(undefined); expect(testContextReports[0]).to.be.an('object'); expect(replaceErrorStackForTest(testContextReports[0].message)).to.equal('Resolve error: SyntaxError: TEST SYNTAX ERROR\n'); @@ -315,11 +349,11 @@ describe('resolve', function () { }); }); - const caseDescribe = (!CASE_SENSITIVE_FS ? describe : describe.skip); + const caseDescribe = !CASE_SENSITIVE_FS ? describe : describe.skip; caseDescribe('case sensitivity', function () { let file; const testContext = utils.testContext({ - 'import/resolve': { 'extensions': ['.jsx'] }, + 'import/resolve': { extensions: ['.jsx'] }, 'import/cache': { lifetime: 0 }, }); const testSettings = testContext.settings; @@ -333,31 +367,30 @@ describe('resolve', function () { }); it('detects case does not match FS', function () { expect(fileExistsWithCaseSync(file, testSettings)) - .to.be.false; + .to.equal(false); }); it('detecting case does not include parent folder path (issue #720)', function () { const f = path.join(process.cwd().toUpperCase(), './tests/files/jsx/MyUnCoolComponent.jsx'); expect(fileExistsWithCaseSync(f, testSettings)) - .to.be.true; + .to.equal(true); }); it('detecting case should include parent folder path', function () { const f = path.join(process.cwd().toUpperCase(), './tests/files/jsx/MyUnCoolComponent.jsx'); expect(fileExistsWithCaseSync(f, testSettings, true)) - .to.be.false; + .to.equal(false); }); }); describe('rename cache correctness', function () { const context = utils.testContext({ - 'import/cache': { 'lifetime': 1 }, + 'import/cache': { lifetime: 1 }, }); - const infiniteContexts = [ '∞', 'Infinity' ].map(inf => [inf, + const infiniteContexts = [ '∞', 'Infinity' ].map((inf) => [inf, utils.testContext({ - 'import/cache': { 'lifetime': inf }, + 'import/cache': { lifetime: inf }, })]); - const pairs = [ ['./CaseyKasem.js', './CASEYKASEM2.js'], ]; @@ -372,7 +405,7 @@ describe('resolve', function () { // settings are part of cache key before('warm up infinite entries', function () { - infiniteContexts.forEach(([,c]) => { + infiniteContexts.forEach(([, c]) => { expect(resolve(original, c)).to.exist; }); }); @@ -384,10 +417,9 @@ describe('resolve', function () { done); }); - before('verify rename', (done) => - fs.exists( - utils.testFilePath(changed), - exists => done(exists ? null : new Error('new file does not exist')))); + before('verify rename', (done) => fs.exists( + utils.testFilePath(changed), + (exists) => done(exists ? null : new Error('new file does not exist')))); it('gets cached values within cache lifetime', function () { // get cached values initially @@ -426,7 +458,8 @@ describe('resolve', function () { fs.rename( utils.testFilePath(changed), utils.testFilePath(original), - done); + done, + ); }); }); }); diff --git a/tests/src/package.js b/tests/src/package.js index f759819758..dd55e2740b 100644 --- a/tests/src/package.js +++ b/tests/src/package.js @@ -38,8 +38,8 @@ describe('package', function () { it('exports all configs', function (done) { fs.readdir(path.join(process.cwd(), 'config'), function (err, files) { if (err) { done(err); return; } - files.filter(isJSFile).forEach(file => { - if (file[0] === '.') return; + files.filter(isJSFile).forEach((file) => { + if (file[0] === '.') { return; } expect(module.configs).to.have.property(path.basename(file, '.js')); }); done(); @@ -66,7 +66,7 @@ describe('package', function () { it('marks deprecated rules in their metadata', function () { expect(module.rules['imports-first'].meta.deprecated).to.be.true; - expect(module.rules['first'].meta.deprecated).not.to.be.true; + expect(module.rules.first.meta.deprecated).not.to.be.true; }); }); diff --git a/tests/src/rules/dynamic-import-chunkname.js b/tests/src/rules/dynamic-import-chunkname.js index 46a1b97afe..73617a6f36 100644 --- a/tests/src/rules/dynamic-import-chunkname.js +++ b/tests/src/rules/dynamic-import-chunkname.js @@ -969,7 +969,7 @@ ruleTester.run('dynamic-import-chunkname', rule, { context('TypeScript', () => { getTSParsers().forEach((typescriptParser) => { - const nodeType = typescriptParser === parsers.TS_OLD || (typescriptParser === parsers.TS_NEW && 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/export.js b/tests/src/rules/export.js index 95093bf4a8..a7f2bec122 100644 --- a/tests/src/rules/export.js +++ b/tests/src/rules/export.js @@ -137,7 +137,6 @@ ruleTester.run('export', rule, { // errors: ['Parsing error: Duplicate export \'bar\''], // }), - // #328: "export * from" does not export a default test({ code: 'export * from "./default-export"', @@ -158,7 +157,6 @@ ruleTester.run('export', rule, { ), }); - context('TypeScript', function () { getTSParsers().forEach((parser) => { const parserConfig = { diff --git a/tests/src/rules/exports-last.js b/tests/src/rules/exports-last.js index 9f01f27f42..d7122e9a00 100644 --- a/tests/src/rules/exports-last.js +++ b/tests/src/rules/exports-last.js @@ -5,7 +5,7 @@ import rule from 'rules/exports-last'; const ruleTester = new RuleTester(); -const error = type => ({ +const error = (type) => ({ message: 'Export statements should appear at the end of the file', type, }); diff --git a/tests/src/rules/extensions.js b/tests/src/rules/extensions.js index 45b4498fe9..ede1a8d88a 100644 --- a/tests/src/rules/extensions.js +++ b/tests/src/rules/extensions.js @@ -31,7 +31,7 @@ ruleTester.run('extensions', rule, { 'import data from "./bar.json"', ].join('\n'), options: [ 'never' ], - settings: { 'import/resolve': { 'extensions': [ '.js', '.jsx', '.json' ] } }, + settings: { 'import/resolve': { extensions: [ '.js', '.jsx', '.json' ] } }, }), test({ @@ -41,7 +41,7 @@ ruleTester.run('extensions', rule, { 'import barhbs from "./bar.hbs"', ].join('\n'), options: [ 'always', { js: 'never', jsx: 'never' } ], - settings: { 'import/resolve': { 'extensions': [ '.js', '.jsx', '.json', '.hbs' ] } }, + settings: { 'import/resolve': { extensions: [ '.js', '.jsx', '.json', '.hbs' ] } }, }), test({ @@ -50,7 +50,7 @@ ruleTester.run('extensions', rule, { 'import pack from "./package"', ].join('\n'), options: [ 'never', { js: 'always', json: 'never' } ], - settings: { 'import/resolve': { 'extensions': [ '.js', '.json' ] } }, + settings: { 'import/resolve': { extensions: [ '.js', '.json' ] } }, }), // unresolved (#271/#295) @@ -96,8 +96,8 @@ ruleTester.run('extensions', rule, { filename: testFilePath('./internal-modules/plugins/plugin.js'), settings: { 'import/resolver': { - 'node': { 'extensions': [ '.js', '.jsx', '.json' ] }, - 'webpack': { 'config': 'webpack.empty.config.js' }, + node: { extensions: [ '.js', '.jsx', '.json' ] }, + webpack: { config: 'webpack.empty.config.js' }, }, }, }), @@ -174,7 +174,7 @@ ruleTester.run('extensions', rule, { 'import packageConfig from "./package"', ].join('\n'), options: [ { json: 'always', js: 'never' } ], - settings: { 'import/resolve': { 'extensions': [ '.js', '.json' ] } }, + settings: { 'import/resolve': { extensions: [ '.js', '.json' ] } }, errors: [ { message: 'Unexpected use of file extension "js" for "a/index.js"', @@ -195,7 +195,7 @@ ruleTester.run('extensions', rule, { 'import data from "./bar.json"', ].join('\n'), options: [ 'never' ], - settings: { 'import/resolve': { 'extensions': [ '.js', '.jsx', '.json' ] } }, + settings: { 'import/resolve': { extensions: [ '.js', '.jsx', '.json' ] } }, errors: [ { message: 'Unexpected use of file extension "js" for "./bar.js"', @@ -211,7 +211,7 @@ ruleTester.run('extensions', rule, { 'import data from "./bar.json"', ].join('\n'), options: [ { json: 'always', js: 'never', jsx: 'never' } ], - settings: { 'import/resolve': { 'extensions': [ '.js', '.jsx', '.json' ] } }, + settings: { 'import/resolve': { extensions: [ '.js', '.jsx', '.json' ] } }, errors: [ { message: 'Unexpected use of file extension "js" for "./bar.js"', @@ -227,7 +227,7 @@ ruleTester.run('extensions', rule, { 'import data from "./bar.json"', ].join('\n'), options: [ { json: 'always', js: 'never', jsx: 'never' } ], - settings: { 'import/resolve': { 'extensions': [ '.jsx', '.json', '.js' ] } }, + settings: { 'import/resolve': { extensions: [ '.jsx', '.json', '.js' ] } }, errors: [ { message: 'Unexpected use of file extension "jsx" for "./bar.jsx"', @@ -246,7 +246,7 @@ ruleTester.run('extensions', rule, { }, ], options: ['never', { js: 'always', jsx: 'always' }], - settings: { 'import/resolve': { 'extensions': ['.coffee', '.js'] } }, + settings: { 'import/resolve': { extensions: ['.coffee', '.js'] } }, }), test({ @@ -256,7 +256,7 @@ ruleTester.run('extensions', rule, { 'import barnone from "./bar"', ].join('\n'), options: [ 'always', { json: 'always', js: 'never', jsx: 'never' } ], - settings: { 'import/resolve': { 'extensions': [ '.js', '.jsx', '.json' ] } }, + settings: { 'import/resolve': { extensions: [ '.js', '.jsx', '.json' ] } }, errors: [ { message: 'Unexpected use of file extension "js" for "./bar.js"', @@ -273,7 +273,7 @@ ruleTester.run('extensions', rule, { 'import barnone from "./bar"', ].join('\n'), options: [ 'never', { json: 'always', js: 'never', jsx: 'never' } ], - settings: { 'import/resolve': { 'extensions': [ '.js', '.jsx', '.json' ] } }, + settings: { 'import/resolve': { extensions: [ '.js', '.jsx', '.json' ] } }, errors: [ { message: 'Unexpected use of file extension "js" for "./bar.js"', @@ -331,7 +331,6 @@ ruleTester.run('extensions', rule, { ], }), - test({ code: ` import foo from './foo.js' diff --git a/tests/src/rules/first.js b/tests/src/rules/first.js index 8892ff3d62..f34f227b2d 100644 --- a/tests/src/rules/first.js +++ b/tests/src/rules/first.js @@ -81,7 +81,7 @@ ruleTester.run('first', rule, { code: "if (true) { console.log(1) }import a from 'b'", errors: 1, output: "import a from 'b'\nif (true) { console.log(1) }", - }), + }), ], }); diff --git a/tests/src/rules/named.js b/tests/src/rules/named.js index 227e242ef8..110cfff52a 100644 --- a/tests/src/rules/named.js +++ b/tests/src/rules/named.js @@ -4,12 +4,11 @@ import path from 'path'; import { CASE_SENSITIVE_FS } from 'eslint-module-utils/resolve'; - const ruleTester = new RuleTester(); const rule = require('rules/named'); function error(name, module, type = 'Identifier') { - return { message: name + ' not found in \'' + module + '\'', type }; + return { message: `${name} not found in '${module}'`, type }; } ruleTester.run('named', rule, { @@ -30,11 +29,10 @@ ruleTester.run('named', rule, { test({ code: 'import {RuleTester} from "./re-export-node_modules"' }), test({ code: 'import { jsxFoo } from "./jsx/AnotherComponent"', - settings: { 'import/resolve': { 'extensions': ['.js', '.jsx'] } } }), + settings: { 'import/resolve': { extensions: ['.js', '.jsx'] } } }), // validate that eslint-disable-line silences this properly - test({ code: 'import {a, b, d} from "./common"; ' + - '// eslint-disable-line named' }), + test({ code: 'import {a, b, d} from "./common"; // eslint-disable-line named' }), test({ code: 'import { foo, bar } from "./re-export-names"' }), @@ -192,7 +190,7 @@ ruleTester.run('named', rule, { }, })), - testVersion('>=7.8.0', () =>({ code: 'const { something } = require("./dynamic-import-in-commonjs")', + testVersion('>=7.8.0', () => ({ code: 'const { something } = require("./dynamic-import-in-commonjs")', parserOptions: { ecmaVersion: 2021 }, options: [{ commonjs: true }], })), @@ -323,7 +321,6 @@ ruleTester.run('named', rule, { errors: ["bap not found in './re-export-default'"], }), - // #328: * exports do not include default test({ code: 'import { default as barDefault } from "./re-export"', @@ -381,7 +378,6 @@ ruleTester.run('named (export *)', rule, { ], }); - context('TypeScript', function () { getTSParsers().forEach((parser) => { const settings = { @@ -459,7 +455,7 @@ context('TypeScript', function () { parser, settings, }), - (source === 'typescript-declare' + source === 'typescript-declare' ? testVersion('> 5', () => ({ code: `import { getFoo } from "./${source}"`, parser, @@ -470,7 +466,7 @@ context('TypeScript', function () { parser, settings, }) - ), + , test({ code: `import { MyEnum } from "./${source}"`, parser, diff --git a/tests/src/rules/namespace.js b/tests/src/rules/namespace.js index 163ff163ea..d368fd3fe9 100644 --- a/tests/src/rules/namespace.js +++ b/tests/src/rules/namespace.js @@ -5,7 +5,6 @@ import flatMap from 'array.prototype.flatmap'; const ruleTester = new RuleTester({ env: { es6: true } }); const rule = require('rules/namespace'); - function error(name, namespace) { return { message: `'${name}' not found in imported namespace '${namespace}'.` }; } @@ -14,13 +13,10 @@ const valid = [ test({ code: 'import "./malformed.js"' }), test({ code: "import * as foo from './empty-folder';" }), - test({ code: 'import * as names from "./named-exports"; ' + - 'console.log((names.b).c); ' }), + test({ code: 'import * as names from "./named-exports"; console.log((names.b).c); ' }), - test({ code: 'import * as names from "./named-exports"; ' + - 'console.log(names.a);' }), - test({ code: 'import * as names from "./re-export-names"; ' + - 'console.log(names.foo);' }), + test({ code: 'import * as names from "./named-exports"; console.log(names.a);' }), + test({ code: 'import * as names from "./re-export-names"; console.log(names.foo);' }), test({ code: "import * as elements from './jsx';", parserOptions: { @@ -61,26 +57,23 @@ const valid = [ test({ code: "import * as foo from './common';" }), // destructuring namespaces - test({ code: 'import * as names from "./named-exports";' + - 'const { a } = names' }), - test({ code: 'import * as names from "./named-exports";' + - 'const { d: c } = names' }), - test({ code: 'import * as names from "./named-exports";' + - 'const { c } = foo\n' + - ' , { length } = "names"\n' + - ' , alt = names' }), + test({ code: 'import * as names from "./named-exports"; const { a } = names' }), + test({ code: 'import * as names from "./named-exports"; const { d: c } = names' }), + test({ + code: ` + import * as names from "./named-exports"; + const { c } = foo, + { length } = "names", + alt = names; + `, + }), // deep destructuring only cares about top level - test({ code: 'import * as names from "./named-exports";' + - 'const { ExportedClass: { length } } = names' }), + test({ code: 'import * as names from "./named-exports"; const { ExportedClass: { length } } = names' }), // detect scope redefinition - test({ code: 'import * as names from "./named-exports";' + - 'function b(names) { const { c } = names }' }), - test({ code: 'import * as names from "./named-exports";' + - 'function b() { let names = null; const { c } = names }' }), - test({ code: 'import * as names from "./named-exports";' + - 'const x = function names() { const { c } = names }' }), - + test({ code: 'import * as names from "./named-exports"; function b(names) { const { c } = names }' }), + test({ code: 'import * as names from "./named-exports"; function b() { let names = null; const { c } = names }' }), + test({ code: 'import * as names from "./named-exports"; const x = function names() { const { c } = names }' }), ///////// // es7 // @@ -101,8 +94,7 @@ const valid = [ // respect hoisting test({ code: - 'function x() { console.log((names.b).c); } ' + - 'import * as names from "./named-exports"; ', + 'function x() { console.log((names.b).c); } import * as names from "./named-exports"; ', }), // names.default is valid export @@ -241,13 +233,11 @@ const valid = [ ]; const invalid = [].concat( - test({ code: "import * as names from './named-exports'; " + - ' console.log(names.c);', - errors: [error('c', 'names')] }), + test({ code: "import * as names from './named-exports'; console.log(names.c)", + errors: [error('c', 'names')] }), - test({ code: "import * as names from './named-exports';" + - " console.log(names['a']);", - errors: ["Unable to validate computed reference to imported namespace 'names'."] }), + test({ code: "import * as names from './named-exports'; console.log(names['a']);", + errors: ["Unable to validate computed reference to imported namespace 'names'."] }), // assignment warning (from no-reassign) test({ code: 'import * as foo from \'./bar\'; foo.foo = \'y\';', @@ -269,8 +259,7 @@ const invalid = [].concat( errors: [{ type: 'Property', message: "'c' not found in imported namespace 'names'." }], }), test({ - code: 'import * as names from "./named-exports";' + - 'const { c: { d } } = names', + code: 'import * as names from "./named-exports"; const { c: { d } } = names', errors: [{ type: 'Property', message: "'c' not found in imported namespace 'names'." }], }), @@ -300,15 +289,11 @@ const invalid = [].concat( // respect hoisting test({ - code: - 'console.log(names.c);' + - "import * as names from './named-exports'; ", + code: `console.log(names.c); import * as names from './named-exports';`, errors: [error('c', 'names')], }), test({ - code: - 'function x() { console.log(names.c) } ' + - "import * as names from './named-exports'; ", + code: `function x() { console.log(names.c) } import * as names from './named-exports';`, errors: [error('c', 'names')], }), @@ -340,12 +325,12 @@ const invalid = [].concat( errors: [ "'e' not found in deeply imported namespace 'b.c'." ], parserOptions: { ecmaVersion: 2022 }, })), -) +); /////////////////////// // deep dereferences // ////////////////////// -;[['deep', require.resolve('espree')], ['deep-es7', parsers.BABEL_OLD]].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 bf91064f85..5e14b570ee 100644 --- a/tests/src/rules/newline-after-import.js +++ b/tests/src/rules/newline-after-import.js @@ -6,9 +6,7 @@ import { version as tsEslintVersion } from 'typescript-eslint-parser/package.jso 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) => { - return `Expected ${count} empty lines after import statement not followed by another import.`; -}; +const IMPORT_ERROR_MESSAGE_MULTIPLE = (count) => `Expected ${count} empty lines after import statement not followed by another import.`; const REQUIRE_ERROR_MESSAGE = 'Expected 1 empty line after require statement not followed by another require.'; const ruleTester = new RuleTester(); @@ -22,7 +20,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { code: ` const x = () => require('baz') , y = () => require('bar')`, - parserOptions: { ecmaVersion: 6 } , + parserOptions: { ecmaVersion: 6 }, }, { code: ` @@ -31,12 +29,12 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { // some comment here `, - parserOptions: { ecmaVersion: 6 } , + parserOptions: { ecmaVersion: 6 }, options: [{ considerComments: true }], }, { code: `const x = () => require('baz') && require('bar')`, - parserOptions: { ecmaVersion: 6 } , + parserOptions: { ecmaVersion: 6 }, }, { code: ` @@ -45,8 +43,8 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { // Some random single line comment var bar = 42; `, - parserOptions: { ecmaVersion: 6 } , - options: [{ 'considerComments': true }], + parserOptions: { ecmaVersion: 6 }, + options: [{ considerComments: true }], }, { code: ` @@ -57,7 +55,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { **/ var bar = 42; `, - parserOptions: { ecmaVersion: 6 } , + parserOptions: { ecmaVersion: 6 }, }, `function x() { require('baz'); }`, `a(require('b'), require('c'), require('d'));`, @@ -122,12 +120,12 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { { code: `import foo from 'foo';\n\n\nvar bar = 'bar';`, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - options: [{ 'count': 2 }], + options: [{ count: 2 }], }, { code: `import foo from 'foo';\n\n\n\n\nvar bar = 'bar';`, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - options: [{ 'count': 4 }], + options: [{ count: 4 }], }, { code: `var foo = require('foo-module');\n\nvar foo = 'bar';`, @@ -136,12 +134,12 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { { code: `var foo = require('foo-module');\n\n\nvar foo = 'bar';`, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - options: [{ 'count': 2 }], + options: [{ count: 2 }], }, { code: `var foo = require('foo-module');\n\n\n\n\nvar foo = 'bar';`, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - options: [{ 'count': 4 }], + options: [{ count: 4 }], }, { code: `require('foo-module');\n\nvar foo = 'bar';`, @@ -202,12 +200,12 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { parser: parsers.BABEL_OLD, }, { - code : `// issue 1004\nimport foo from 'foo';\n\n@SomeDecorator(foo)\nexport default class Test {}`, + code: `// issue 1004\nimport foo from 'foo';\n\n@SomeDecorator(foo)\nexport default class Test {}`, parserOptions: { sourceType: 'module' }, parser: parsers.BABEL_OLD, }, { - code : `// issue 1004\nconst foo = require('foo');\n\n@SomeDecorator(foo)\nexport default class Test {}`, + code: `// issue 1004\nconst foo = require('foo');\n\n@SomeDecorator(foo)\nexport default class Test {}`, parserOptions: { sourceType: 'module' }, parser: parsers.BABEL_OLD, }, @@ -296,7 +294,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { **/ var bar = 42; `, - parserOptions: { ecmaVersion: 2015, sourceType: 'module' } , + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { code: ` @@ -308,8 +306,8 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { **/ var bar = 42; `, - parserOptions: { ecmaVersion: 2015, sourceType: 'module' } , - options: [{ 'considerComments': true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + options: [{ considerComments: true }], }, { code: ` @@ -319,7 +317,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { // Some random single line comment var bar = 42; `, - parserOptions: { ecmaVersion: 2015, sourceType: 'module' } , + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, ), @@ -344,7 +342,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { message: IMPORT_ERROR_MESSAGE, } ], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, - options: [{ 'considerComments': true }], + options: [{ considerComments: true }], }, { code: ` @@ -370,8 +368,8 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { column: 9, message: IMPORT_ERROR_MESSAGE, } ], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' } , - options: [{ 'considerComments': true }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + options: [{ considerComments: true }], }, { code: ` @@ -391,8 +389,8 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { column: 9, message: IMPORT_ERROR_MESSAGE, } ], - parserOptions: { ecmaVersion: 2015, sourceType: 'module' } , - options: [{ 'considerComments': true, 'count': 1 }], + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + options: [{ considerComments: true, count: 1 }], }, { code: `import foo from 'foo';\nexport default function() {};`, @@ -407,7 +405,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { { code: `import foo from 'foo';\n\nexport default function() {};`, output: `import foo from 'foo';\n\n\nexport default function() {};`, - options: [{ 'count': 2 }], + options: [{ count: 2 }], errors: [ { line: 1, column: 1, @@ -428,7 +426,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { { code: `import foo from 'foo';\nexport default function() {};`, output: `import foo from 'foo';\n\nexport default function() {};`, - options: [{ 'count': 1 }], + options: [{ count: 1 }], errors: [ { line: 1, column: 1, diff --git a/tests/src/rules/no-commonjs.js b/tests/src/rules/no-commonjs.js index a2e3464ca7..baa3b907f6 100644 --- a/tests/src/rules/no-commonjs.js +++ b/tests/src/rules/no-commonjs.js @@ -19,12 +19,12 @@ ruleTester.run('no-commonjs', require('rules/no-commonjs'), { { code: 'export default "x"', parserOptions: { ecmaVersion: 2015, sourceType: 'module' } }, { code: 'export function house() {}', parserOptions: { ecmaVersion: 2015, sourceType: 'module' } }, { - code: - 'function someFunc() {\n'+ - ' const exports = someComputation();\n'+ - '\n'+ - ' expect(exports.someProp).toEqual({ a: \'value\' });\n'+ - '}', + code: ` + function someFunc() { + const exports = someComputation(); + expect(exports.someProp).toEqual({ a: 'value' }); + } + `, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, @@ -68,7 +68,7 @@ ruleTester.run('no-commonjs', require('rules/no-commonjs'), { invalid: [ // imports - ...(semver.satisfies(eslintPkg.version, '< 4.0.0') ? [] : [ + ...semver.satisfies(eslintPkg.version, '< 4.0.0') ? [] : [ { code: 'var x = require("x")', output: 'var x = require("x")', errors: [ { message: IMPORT_MESSAGE }] }, { code: 'x = require("x")', output: 'x = require("x")', errors: [ { message: IMPORT_MESSAGE }] }, { code: 'require("x")', output: 'require("x")', errors: [ { message: IMPORT_MESSAGE }] }, @@ -93,7 +93,7 @@ ruleTester.run('no-commonjs', require('rules/no-commonjs'), { output: 'try { require("x") } catch (error) {}', errors: [ { message: IMPORT_MESSAGE }], }, - ]), + ], // exports { code: 'exports.face = "palm"', output: 'exports.face = "palm"', errors: [ { message: EXPORT_MESSAGE }] }, diff --git a/tests/src/rules/no-cycle.js b/tests/src/rules/no-cycle.js index 155f257b71..d2adbf61f9 100644 --- a/tests/src/rules/no-cycle.js +++ b/tests/src/rules/no-cycle.js @@ -6,9 +6,9 @@ import flatMap from 'array.prototype.flatmap'; const ruleTester = new RuleTester(); const rule = require('rules/no-cycle'); -const error = message => ({ message }); +const error = (message) => ({ message }); -const test = def => _test(Object.assign(def, { +const test = (def) => _test(Object.assign(def, { filename: testFilePath('./cycles/depth-zero.js'), })); const testVersion = (specifier, t) => _testVersion(specifier, () => Object.assign(t(), { diff --git a/tests/src/rules/no-deprecated.js b/tests/src/rules/no-deprecated.js index 290946735f..318ea7c368 100644 --- a/tests/src/rules/no-deprecated.js +++ b/tests/src/rules/no-deprecated.js @@ -38,7 +38,6 @@ ruleTester.run('no-deprecated', rule, { code: "import { deepDep } from './deep-deprecated'; function x(deepDep) { console.log(deepDep.MY_TERRIBLE_ACTION) }", }), - ...SYNTAX_CASES, ], invalid: [ @@ -210,18 +209,20 @@ describe('TypeScript', function () { ruleTester.run(parser, rule, { valid: [ - test(Object.assign({ + test({ code: 'import * as hasDeprecated from \'./ts-deprecated.ts\'', - }, parserConfig)), + ...parserConfig, + }), ], invalid: [ - test(Object.assign({ + test({ code: 'import { foo } from \'./ts-deprecated.ts\'; console.log(foo())', errors: [ { type: 'ImportSpecifier', message: 'Deprecated: don\'t use this!' }, { type: 'Identifier', message: 'Deprecated: don\'t use this!' }, - ] }, - parserConfig)), + ], + ...parserConfig, + }), ], }); }); diff --git a/tests/src/rules/no-duplicates.js b/tests/src/rules/no-duplicates.js index ac76c3070a..33e1e632e2 100644 --- a/tests/src/rules/no-duplicates.js +++ b/tests/src/rules/no-duplicates.js @@ -12,7 +12,7 @@ const rule = require('rules/no-duplicates'); // autofix only possible with eslint 4+ const test = semver.satisfies(eslintPkg.version, '< 4') - ? t => testUtil(Object.assign({}, t, { output: t.code })) + ? (t) => testUtil({ ...t, output: t.code }) : testUtil; ruleTester.run('no-duplicates', rule, { @@ -22,8 +22,7 @@ ruleTester.run('no-duplicates', rule, { test({ code: "import { x } from './foo'; import { y } from './bar'" }), // #86: every unresolved module should not show up as 'null' and duplicate - test({ code: 'import foo from "234artaf";' + - 'import { shoop } from "234q25ad"' }), + test({ code: 'import foo from "234artaf"; import { shoop } from "234q25ad"' }), // #225: ignore duplicate if is a flow type import test({ @@ -34,12 +33,12 @@ ruleTester.run('no-duplicates', rule, { // #1107: Using different query strings that trigger different webpack loaders. test({ code: "import x from './bar?optionX'; import y from './bar?optionY';", - options: [{ 'considerQueryString': true }], + options: [{ considerQueryString: true }], settings: { 'import/resolver': 'webpack' }, }), test({ code: "import x from './foo'; import y from './bar';", - options: [{ 'considerQueryString': true }], + options: [{ considerQueryString: true }], settings: { 'import/resolver': 'webpack' }, }), @@ -68,10 +67,11 @@ ruleTester.run('no-duplicates', rule, { test({ code: "import { x } from './bar'; import { y } from 'bar';", output: "import { x , y } from './bar'; ", - settings: { 'import/resolve': { - paths: [path.join( process.cwd() - , 'tests', 'files', - )] } }, + settings: { + 'import/resolve': { + paths: [path.join(process.cwd(), 'tests', 'files')], + }, + }, errors: 2, // path ends up hardcoded }), @@ -90,7 +90,7 @@ ruleTester.run('no-duplicates', rule, { // #1107: Using same query strings that trigger the same loader. test({ code: "import x from './bar?optionX'; import y from './bar.js?optionX';", - options: [{ 'considerQueryString': true }], + options: [{ considerQueryString: true }], settings: { 'import/resolver': 'webpack' }, errors: 2, // path ends up hardcoded }), @@ -132,8 +132,8 @@ ruleTester.run('no-duplicates', rule, { }), // These test cases use duplicate import identifiers, which causes a fatal parsing error using ESPREE (default) and TS_OLD. - ...flatMap([parsers.BABEL_OLD, parsers.TS_NEW], parser => { - if (!parser) return []; // TS_NEW is not always available + ...flatMap([parsers.BABEL_OLD, parsers.TS_NEW], (parser) => { + if (!parser) { return []; } // TS_NEW is not always available return [ // #2347: duplicate identifiers should be removed test({ diff --git a/tests/src/rules/no-dynamic-require.js b/tests/src/rules/no-dynamic-require.js index 0b141ccd76..e316470ec8 100644 --- a/tests/src/rules/no-dynamic-require.js +++ b/tests/src/rules/no-dynamic-require.js @@ -30,10 +30,9 @@ ruleTester.run('no-dynamic-require', rule, { //dynamic import ...flatMap([parsers.ESPREE, parsers.BABEL_OLD], (parser) => { - const _test = - parser === parsers.ESPREE - ? (testObj) => testVersion('>= 6.2.0', () => testObj) - : (testObj) => test(testObj); + const _test = parser === parsers.ESPREE + ? (testObj) => testVersion('>= 6.2.0', () => testObj) + : (testObj) => test(testObj); return [].concat( _test({ code: 'import("foo")', @@ -143,10 +142,9 @@ ruleTester.run('no-dynamic-require', rule, { // dynamic import ...flatMap([parsers.ESPREE, parsers.BABEL_OLD], (parser) => { - const _test = - parser === parsers.ESPREE - ? (testObj) => testVersion('>= 6.2.0', () => testObj) - : (testObj) => test(testObj); + const _test = parser === parsers.ESPREE + ? (testObj) => testVersion('>= 6.2.0', () => testObj) + : (testObj) => test(testObj); return [].concat( _test({ code: 'import("../" + name)', diff --git a/tests/src/rules/no-empty-named-blocks.js b/tests/src/rules/no-empty-named-blocks.js index 87a0a3e7c9..f65e5a2045 100644 --- a/tests/src/rules/no-empty-named-blocks.js +++ b/tests/src/rules/no-empty-named-blocks.js @@ -5,9 +5,8 @@ import { RuleTester } from 'eslint'; const ruleTester = new RuleTester(); const rule = require('rules/no-empty-named-blocks'); - function generateSuggestionsTestCases(cases, parser) { - return cases.map(code => test({ + return cases.map((code) => test({ code, parser, errors: [{ diff --git a/tests/src/rules/no-extraneous-dependencies.js b/tests/src/rules/no-extraneous-dependencies.js index 84aa8bb35d..6f4e710f9d 100644 --- a/tests/src/rules/no-extraneous-dependencies.js +++ b/tests/src/rules/no-extraneous-dependencies.js @@ -308,7 +308,7 @@ ruleTester.run('no-extraneous-dependencies', rule, { code: 'import foo from "foo"', options: [{ packageDir: packageDirWithSyntaxError }], errors: [{ - message: 'The package.json file could not be parsed: ' + packageFileWithSyntaxErrorMessage, + message: `The package.json file could not be parsed: ${packageFileWithSyntaxErrorMessage}`, }], }), test({ @@ -422,31 +422,29 @@ describe('TypeScript', () => { ruleTester.run('no-extraneous-dependencies', rule, { valid: [ - test(Object.assign({ + test({ code: 'import type T from "a";', options: [{ packageDir: packageDirWithTypescriptDevDependencies, devDependencies: false }], - }, parserConfig)), + ...parserConfig, + }), ], invalid: [ - test(Object.assign({ + test({ code: 'import T from "a";', options: [{ packageDir: packageDirWithTypescriptDevDependencies, devDependencies: false }], - errors: [{ - message: "'a' should be listed in the project's dependencies, not devDependencies.", - }], - }, parserConfig)), + errors: [{ message: "'a' should be listed in the project's dependencies, not devDependencies." }], + ...parserConfig, + }), - test(Object.assign({ - code: 'import type T from "a";', + test({ code: 'import type T from "a";', options: [{ packageDir: packageDirWithTypescriptDevDependencies, devDependencies: false, includeTypes: true, }], - errors: [{ - message: "'a' should be listed in the project's dependencies, not devDependencies.", - }], - }, parserConfig)), + errors: [{ message: "'a' should be listed in the project's dependencies, not devDependencies." }], + ...parserConfig, + }), ], }); }); diff --git a/tests/src/rules/no-import-module-exports.js b/tests/src/rules/no-import-module-exports.js index 81faceba98..c2bf7ed132 100644 --- a/tests/src/rules/no-import-module-exports.js +++ b/tests/src/rules/no-import-module-exports.js @@ -9,8 +9,7 @@ const ruleTester = new RuleTester({ const rule = require('rules/no-import-module-exports'); const error = { - message: `Cannot use import declarations in modules that export using CommonJS ` + - `(module.exports = 'foo' or exports.bar = 'hi')`, + message: `Cannot use import declarations in modules that export using CommonJS (module.exports = 'foo' or exports.bar = 'hi')`, type: 'ImportDeclaration', }; diff --git a/tests/src/rules/no-named-as-default-member.js b/tests/src/rules/no-named-as-default-member.js index 53cba230ba..1773176f4f 100644 --- a/tests/src/rules/no-named-as-default-member.js +++ b/tests/src/rules/no-named-as-default-member.js @@ -25,40 +25,28 @@ ruleTester.run('no-named-as-default-member', rule, { test({ code: 'import bar from "./bar"; const foo = bar.foo;', errors: [{ - message: ( - 'Caution: `bar` also has a named export `foo`. ' + - 'Check if you meant to write `import {foo} from \'./bar\'` instead.' - ), + message: 'Caution: `bar` also has a named export `foo`. Check if you meant to write `import {foo} from \'./bar\'` instead.', type: 'MemberExpression', }], }), test({ code: 'import bar from "./bar"; bar.foo();', errors: [{ - message: ( - 'Caution: `bar` also has a named export `foo`. ' + - 'Check if you meant to write `import {foo} from \'./bar\'` instead.' - ), + message: 'Caution: `bar` also has a named export `foo`. Check if you meant to write `import {foo} from \'./bar\'` instead.', type: 'MemberExpression', }], }), test({ code: 'import bar from "./bar"; const {foo} = bar;', errors: [{ - message: ( - 'Caution: `bar` also has a named export `foo`. ' + - 'Check if you meant to write `import {foo} from \'./bar\'` instead.' - ), + message: 'Caution: `bar` also has a named export `foo`. Check if you meant to write `import {foo} from \'./bar\'` instead.', type: 'Identifier', }], }), test({ code: 'import bar from "./bar"; const {foo: foo2, baz} = bar;', errors: [{ - message: ( - 'Caution: `bar` also has a named export `foo`. ' + - 'Check if you meant to write `import {foo} from \'./bar\'` instead.' - ), + message: 'Caution: `bar` also has a named export `foo`. Check if you meant to write `import {foo} from \'./bar\'` instead.', type: 'Identifier', }], }), @@ -66,10 +54,7 @@ ruleTester.run('no-named-as-default-member', rule, { 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.' - ), + 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-nodejs-modules.js b/tests/src/rules/no-nodejs-modules.js index 9be605709a..b25eb0ce85 100644 --- a/tests/src/rules/no-nodejs-modules.js +++ b/tests/src/rules/no-nodejs-modules.js @@ -6,7 +6,7 @@ const isCore = require('is-core-module'); const ruleTester = new RuleTester(); const rule = require('rules/no-nodejs-modules'); -const error = message => ({ +const error = (message) => ({ message, }); @@ -69,7 +69,7 @@ ruleTester.run('no-nodejs-modules', rule, { allow: ['node:events'], }], }), - ]: [], + ] : [], isCore('node:path') ? [ test({ code: 'import path from "node:path"', diff --git a/tests/src/rules/no-relative-parent-imports.js b/tests/src/rules/no-relative-parent-imports.js index 3050498026..bfd4e16bcd 100644 --- a/tests/src/rules/no-relative-parent-imports.js +++ b/tests/src/rules/no-relative-parent-imports.js @@ -2,7 +2,7 @@ import { RuleTester } from 'eslint'; import rule from 'rules/no-relative-parent-imports'; import { parsers, test as _test, testFilePath } from '../utils'; -const test = def => _test(Object.assign(def, { +const test = (def) => _test(Object.assign(def, { filename: testFilePath('./internal-modules/plugins/plugin2/index.js'), parser: parsers.BABEL_OLD, })); diff --git a/tests/src/rules/no-restricted-paths.js b/tests/src/rules/no-restricted-paths.js index 81182189f2..a83a804a0a 100644 --- a/tests/src/rules/no-restricted-paths.js +++ b/tests/src/rules/no-restricted-paths.js @@ -713,7 +713,7 @@ ruleTester.run('no-restricted-paths', rule, { }); context('Typescript', function () { - getTSParsers().forEach(parser => { + getTSParsers().forEach((parser) => { const settings = { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -933,8 +933,7 @@ context('Typescript', function () { }], }], errors: [{ - message: 'Restricted path exceptions must be descendants of the configured ' + - '`from` path for that zone.', + message: 'Restricted path exceptions must be descendants of the configured `from` path for that zone.', line: 1, column: 20, }], diff --git a/tests/src/rules/no-unassigned-import.js b/tests/src/rules/no-unassigned-import.js index 8724b80d30..f96808cbcc 100644 --- a/tests/src/rules/no-unassigned-import.js +++ b/tests/src/rules/no-unassigned-import.js @@ -29,48 +29,48 @@ ruleTester.run('no-unassigned-import', rule, { test({ code: 'require("lodash")()' }), test({ code: 'import "app.css"', - options: [{ 'allow': ['**/*.css'] }], + options: [{ allow: ['**/*.css'] }], }), test({ code: 'import "app.css";', - options: [{ 'allow': ['*.css'] }], + options: [{ allow: ['*.css'] }], }), test({ code: 'import "./app.css"', - options: [{ 'allow': ['**/*.css'] }], + options: [{ allow: ['**/*.css'] }], }), test({ code: 'import "foo/bar"', - options: [{ 'allow': ['foo/**'] }], + options: [{ allow: ['foo/**'] }], }), test({ code: 'import "foo/bar"', - options: [{ 'allow': ['foo/bar'] }], + options: [{ allow: ['foo/bar'] }], }), test({ code: 'import "../dir/app.css"', - options: [{ 'allow': ['**/*.css'] }], + options: [{ allow: ['**/*.css'] }], }), test({ code: 'import "../dir/app.js"', - options: [{ 'allow': ['**/dir/**'] }], + options: [{ allow: ['**/dir/**'] }], }), test({ code: 'require("./app.css")', - options: [{ 'allow': ['**/*.css'] }], + options: [{ allow: ['**/*.css'] }], }), test({ code: 'import "babel-register"', - options: [{ 'allow': ['babel-register'] }], + options: [{ allow: ['babel-register'] }], }), test({ code: 'import "./styles/app.css"', - options: [{ 'allow': ['src/styles/**'] }], + options: [{ allow: ['src/styles/**'] }], filename: path.join(process.cwd(), 'src/app.js'), }), test({ code: 'import "../scripts/register.js"', - options: [{ 'allow': ['src/styles/**', '**/scripts/*.js'] }], + options: [{ allow: ['src/styles/**', '**/scripts/*.js'] }], filename: path.join(process.cwd(), 'src/app.js'), }), ], @@ -85,22 +85,22 @@ ruleTester.run('no-unassigned-import', rule, { }), test({ code: 'import "./app.css"', - options: [{ 'allow': ['**/*.js'] }], + options: [{ allow: ['**/*.js'] }], errors: [error], }), test({ code: 'import "./app.css"', - options: [{ 'allow': ['**/dir/**'] }], + options: [{ allow: ['**/dir/**'] }], errors: [error], }), test({ code: 'require("./app.css")', - options: [{ 'allow': ['**/*.js'] }], + options: [{ allow: ['**/*.js'] }], errors: [error], }), test({ code: 'import "./styles/app.css"', - options: [{ 'allow': ['styles/*.css'] }], + options: [{ allow: ['styles/*.css'] }], filename: path.join(process.cwd(), 'src/app.js'), errors: [error], }), diff --git a/tests/src/rules/no-unresolved.js b/tests/src/rules/no-unresolved.js index 024e8965ae..109099c389 100644 --- a/tests/src/rules/no-unresolved.js +++ b/tests/src/rules/no-unresolved.js @@ -17,7 +17,7 @@ function runResolverTests(resolver) { ...specs, settings: { ...specs.settings, - 'import/resolver': resolver, + 'import/resolver': resolver, 'import/cache': { lifetime: 0 }, }, }); @@ -161,7 +161,7 @@ function runResolverTests(resolver) { message: "Unable to resolve path to module './empty-folder'.", type: 'Literal', }, - ], + ], }), // sanity check that this module is _not_ found without proper settings @@ -185,7 +185,7 @@ function runResolverTests(resolver) { parser: parsers.BABEL_OLD, }), - rest({ + rest({ code: 'export { foo } from "./does-not-exist"', errors: ["Unable to resolve path to module './does-not-exist'."], }), @@ -359,7 +359,7 @@ ruleTester.run('no-unresolved (import/resolve legacy)', rule, { code: "import { DEEP } from 'in-alternate-root';", settings: { 'import/resolve': { - 'paths': [ + paths: [ path.join(process.cwd(), 'tests', 'files', 'alternate-root'), ], }, @@ -373,14 +373,14 @@ ruleTester.run('no-unresolved (import/resolve legacy)', rule, { paths: [ path.join('tests', 'files', 'src-root'), path.join('tests', 'files', 'alternate-root'), - ], + ], }, }, }), test({ code: 'import * as foo from "jsx-module/foo"', - settings: { 'import/resolve': { 'extensions': ['.jsx'] } }, + settings: { 'import/resolve': { extensions: ['.jsx'] } }, }), ], @@ -417,7 +417,6 @@ ruleTester.run('no-unresolved (webpack-specific)', rule, { ], }); - ruleTester.run('no-unresolved ignore list', rule, { valid: [ test({ @@ -440,7 +439,7 @@ ruleTester.run('no-unresolved ignore list', rule, { }), ], - invalid:[ + invalid: [ test({ code: 'import "./test.gif"', options: [{ ignore: ['.png$'] }], @@ -458,7 +457,7 @@ ruleTester.run('no-unresolved ignore list', rule, { ruleTester.run('no-unresolved unknown resolver', rule, { valid: [], - invalid:[ + invalid: [ // logs resolver load error test({ @@ -490,7 +489,7 @@ ruleTester.run('no-unresolved electron', rule, { settings: { 'import/core-modules': ['electron'] }, }), ], - invalid:[ + invalid: [ test({ code: 'import "electron"', errors: [`Unable to resolve path to module 'electron'.`], @@ -500,7 +499,7 @@ ruleTester.run('no-unresolved electron', rule, { ruleTester.run('no-unresolved syntax verification', rule, { valid: SYNTAX_CASES, - invalid:[], + invalid: [], }); // https://github.com/import-js/eslint-plugin-import/issues/2024 @@ -522,7 +521,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 !== parsers.TS_OLD).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 87714b599b..936123ab71 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -20,7 +20,7 @@ const typescriptRuleTester = new RuleTester(typescriptConfig); const jsxRuleTester = new RuleTester(jsxConfig); const rule = require('rules/no-unused-modules'); -const error = message => ({ message }); +const error = (message) => ({ message }); const missingExportsOptions = [{ missingExports: true, @@ -109,7 +109,6 @@ ruleTester.run('no-unused-modules', rule, { ], }); - // tests for exports ruleTester.run('no-unused-modules', rule, { valid: [ @@ -247,7 +246,6 @@ ruleTester.run('no-unused-modules', rule, { ], }); - describe('dynamic imports', function () { if (semver.satisfies(eslintPkg.version, '< 6')) { beforeEach(function () { @@ -817,7 +815,6 @@ describe('test behavior for new file', () => { ], }); - describe('test behavior for new file', () => { before(() => { fs.writeFileSync(testFilePath('./no-unused-modules/file-added-1.js'), '', { encoding: 'utf8' }); @@ -1382,7 +1379,7 @@ describe('supports flat eslint', { skip: !FlatRuleTester }, () => { flatRuleTester.run('no-unused-modules', rule, { valid: [{ options: unusedExportsOptions, - code: 'import { o2 } from "./file-o";export default () => 12', + code: 'import { o2 } from "./file-o"; export default () => 12', filename: testFilePath('./no-unused-modules/file-a.js'), }], invalid: [{ diff --git a/tests/src/rules/no-webpack-loader-syntax.js b/tests/src/rules/no-webpack-loader-syntax.js index 2b841e18a3..05ad242f50 100644 --- a/tests/src/rules/no-webpack-loader-syntax.js +++ b/tests/src/rules/no-webpack-loader-syntax.js @@ -88,12 +88,8 @@ context('TypeScript', function () { 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)), + test({ code: 'import { foo } from\nalert()', ...parserConfig }), + test({ code: 'import foo from\nalert()', ...parserConfig }), ], invalid: [], }); diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index 291dae33b0..8d0b315e71 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -7,7 +7,6 @@ import flatMap from 'array.prototype.flatmap'; import { resolve } from 'path'; import { default as babelPresetFlow } from 'babel-preset-flow'; - const ruleTester = new RuleTester(); const flowRuleTester = new RuleTester({ parser: resolve(__dirname, '../../../node_modules/babel-eslint'), @@ -22,7 +21,7 @@ const flowRuleTester = new RuleTester({ const rule = require('rules/order'); function withoutAutofixOutput(test) { - return Object.assign({}, test, { output: test.code }); + return { ...test, output: test.code }; } ruleTester.run('order', rule, { @@ -193,7 +192,7 @@ ruleTester.run('order', rule, { var index = require('./'); `, }), - ...flatMap(getTSParsers(), parser => [ + ...flatMap(getTSParsers(), (parser) => [ // Export equals expressions should be on top alongside with ordinary import-statements. test({ code: ` @@ -829,7 +828,7 @@ ruleTester.run('order', rule, { pathGroupsExcludedImportTypes: [], }], }), - ...flatMap(getTSParsers, parser => [ + ...flatMap(getTSParsers, (parser) => [ // Order of the `import ... = require(...)` syntax test({ code: ` @@ -950,18 +949,18 @@ ruleTester.run('order', rule, { options: [ { 'newlines-between': 'always', - 'distinctGroup': true, - 'pathGroupsExcludedImportTypes': [], - 'pathGroups': [ + distinctGroup: true, + pathGroupsExcludedImportTypes: [], + pathGroups: [ { - 'pattern': 'a', - 'group': 'external', - 'position': 'before', + pattern: 'a', + group: 'external', + position: 'before', }, { - 'pattern': 'b', - 'group': 'external', - 'position': 'after', + pattern: 'b', + group: 'external', + position: 'after', }, ], }, @@ -977,18 +976,18 @@ ruleTester.run('order', rule, { options: [ { 'newlines-between': 'always', - 'distinctGroup': false, - 'pathGroupsExcludedImportTypes': [], - 'pathGroups': [ + distinctGroup: false, + pathGroupsExcludedImportTypes: [], + pathGroups: [ { - 'pattern': 'a', - 'group': 'external', - 'position': 'before', + pattern: 'a', + group: 'external', + position: 'before', }, { - 'pattern': 'b', - 'group': 'external', - 'position': 'after', + pattern: 'b', + group: 'external', + position: 'after', }, ], }, @@ -1005,17 +1004,17 @@ ruleTester.run('order', rule, { options: [ { 'newlines-between': 'always', - 'distinctGroup': false, - 'pathGroupsExcludedImportTypes': [], - 'pathGroups': [ + distinctGroup: false, + pathGroupsExcludedImportTypes: [], + pathGroups: [ { - 'pattern': 'a', - 'group': 'external', + pattern: 'a', + group: 'external', }, { - 'pattern': 'b', - 'group': 'internal', - 'position': 'before', + pattern: 'b', + group: 'internal', + position: 'before', }, ], }, @@ -1041,53 +1040,53 @@ ruleTester.run('order', rule, { `, options: [ { - 'alphabetize': { - 'caseInsensitive': false, - 'order': 'asc', + alphabetize: { + caseInsensitive: false, + order: 'asc', }, 'newlines-between': 'always', - 'groups': [ + groups: [ ['builtin', 'external', 'internal', 'unknown', 'object', 'type'], 'parent', ['sibling', 'index'], ], - 'distinctGroup': false, - 'pathGroupsExcludedImportTypes': [], - 'pathGroups': [ + distinctGroup: false, + pathGroupsExcludedImportTypes: [], + pathGroups: [ { - 'pattern': './', - 'group': 'sibling', - 'position': 'before', + pattern: './', + group: 'sibling', + position: 'before', }, { - 'pattern': '.', - 'group': 'sibling', - 'position': 'before', + pattern: '.', + group: 'sibling', + position: 'before', }, { - 'pattern': '..', - 'group': 'parent', - 'position': 'before', + pattern: '..', + group: 'parent', + position: 'before', }, { - 'pattern': '../', - 'group': 'parent', - 'position': 'before', + pattern: '../', + group: 'parent', + position: 'before', }, { - 'pattern': '[a-z]*', - 'group': 'external', - 'position': 'before', + pattern: '[a-z]*', + group: 'external', + position: 'before', }, { - 'pattern': '../[a-z]*', - 'group': 'parent', - 'position': 'before', + pattern: '../[a-z]*', + group: 'parent', + position: 'before', }, { - 'pattern': './[a-z]*', - 'group': 'sibling', - 'position': 'before', + pattern: './[a-z]*', + group: 'sibling', + position: 'before', }, ], }, @@ -1101,7 +1100,7 @@ ruleTester.run('order', rule, { `, options: [ { - 'alphabetize': { order: 'asc', orderImportKind: 'asc', 'caseInsensitive': true }, + alphabetize: { order: 'asc', orderImportKind: 'asc', caseInsensitive: true }, }, ], }), @@ -1179,12 +1178,8 @@ ruleTester.run('order', rule, { }), // fix order with windows end of lines test({ - code: - `/* comment0 */ /* comment1 */ var async = require('async'); /* comment2 */` + `\r\n` + - `/* comment3 */ var fs = require('fs'); /* comment4 */` + `\r\n`, - output: - `/* comment3 */ var fs = require('fs'); /* comment4 */` + `\r\n` + - `/* comment0 */ /* comment1 */ var async = require('async'); /* comment2 */` + `\r\n`, + code: `/* comment0 */ /* comment1 */ var async = require('async'); /* comment2 */` + `\r\n` + `/* comment3 */ var fs = require('fs'); /* comment4 */` + `\r\n`, + output: `/* comment3 */ var fs = require('fs'); /* comment4 */` + `\r\n` + `/* comment0 */ /* comment1 */ var async = require('async'); /* comment2 */` + `\r\n`, errors: [{ message: '`fs` import should occur before import of `async`', }], @@ -1558,7 +1553,7 @@ ruleTester.run('order', rule, { message: '`fs` import should occur after import of `../foo/bar`', }], }), - ...flatMap(getTSParsers(), parser => [ + ...flatMap(getTSParsers(), (parser) => [ // Order of the `import ... = require(...)` syntax test({ code: ` @@ -2676,8 +2671,8 @@ ruleTester.run('order', rule, { options: [ { 'newlines-between': 'always', - 'distinctGroup': false, - 'pathGroupsExcludedImportTypes': [], + distinctGroup: false, + pathGroupsExcludedImportTypes: [], }, ], errors: [{ @@ -2698,18 +2693,18 @@ ruleTester.run('order', rule, { options: [ { 'newlines-between': 'always', - 'distinctGroup': false, - 'pathGroupsExcludedImportTypes': [], - 'pathGroups': [ + distinctGroup: false, + pathGroupsExcludedImportTypes: [], + pathGroups: [ { - 'pattern': 'a', - 'group': 'external', - 'position': 'before', + pattern: 'a', + group: 'external', + position: 'before', }, { - 'pattern': 'c', - 'group': 'external', - 'position': 'after', + pattern: 'c', + group: 'external', + position: 'after', }, ], }, @@ -2743,7 +2738,6 @@ ruleTester.run('order', rule, { ].filter((t) => !!t), }); - context('TypeScript', function () { getNonDefaultParsers() // Type-only imports were added in TypeScript ESTree 2.23.0 @@ -3182,7 +3176,7 @@ context('TypeScript', function () { `, errors: [{ message: '`fs` type import should occur before type import of `path`', - },{ + }, { message: '`fs` type import should occur before type import of `path`', }], ...parserConfig, @@ -3303,34 +3297,34 @@ flowRuleTester.run('order', rule, { `, options: [ { - 'groups': [ + groups: [ ['builtin', 'external'], 'internal', ['sibling', 'parent'], 'object', 'type', ], - 'pathGroups': [ + pathGroups: [ { - 'pattern': 'react', - 'group': 'builtin', - 'position': 'before', - 'patternOptions': { - 'matchBase': true, + pattern: 'react', + group: 'builtin', + position: 'before', + patternOptions: { + matchBase: true, }, }, { - 'pattern': '*.+(css|svg)', - 'group': 'type', - 'position': 'after', - 'patternOptions': { - 'matchBase': true, + pattern: '*.+(css|svg)', + group: 'type', + position: 'after', + patternOptions: { + matchBase: true, }, }, ], - 'pathGroupsExcludedImportTypes': ['react'], - 'alphabetize': { - 'order': 'asc', + pathGroupsExcludedImportTypes: ['react'], + alphabetize: { + order: 'asc', }, 'newlines-between': 'always', }, diff --git a/tests/src/rules/prefer-default-export.js b/tests/src/rules/prefer-default-export.js index ae7c16a40e..a7310445b5 100644 --- a/tests/src/rules/prefer-default-export.js +++ b/tests/src/rules/prefer-default-export.js @@ -362,15 +362,15 @@ context('TypeScript', function () { ...parserConfig, }), semver.satisfies(tsEslintVersion, '>= 22') ? test({ - code: 'export type foo = string /* ' + parser.replace(process.cwd(), '$$PWD') + '*/', + code: `export type foo = string /* ${parser.replace(process.cwd(), '$$PWD')}*/`, ...parserConfig, }) : [], semver.satisfies(tsEslintVersion, '> 20') ? test({ - code: 'export interface foo { bar: string; } /* ' + parser.replace(process.cwd(), '$$PWD') + '*/', + code: `export interface foo { bar: string; } /* ${parser.replace(process.cwd(), '$$PWD')}*/`, ...parserConfig, }) : [], test({ - code: 'export interface foo { bar: string; }; export function goo() {} /* ' + parser.replace(process.cwd(), '$$PWD') + '*/', + code: `export interface foo { bar: string; }; export function goo() {} /* ${parser.replace(process.cwd(), '$$PWD')}*/`, ...parserConfig, }), ), diff --git a/tests/src/utils.js b/tests/src/utils.js index b82883a6f4..d5215b02e3 100644 --- a/tests/src/utils.js +++ b/tests/src/utils.js @@ -50,14 +50,15 @@ export function test(t) { if (arguments.length !== 1) { throw new SyntaxError('`test` requires exactly one object argument'); } - return Object.assign({ + return { filename: FILENAME, - }, t, { - parserOptions: Object.assign({ + ...t, + parserOptions: { sourceType: 'module', ecmaVersion: 9, - }, t.parserOptions), - }); + ...t.parserOptions, + }, + }; } export function testContext(settings) { @@ -133,5 +134,4 @@ export const SYNTAX_CASES = [ test({ code: 'import { foo } from "./ignore.invalid.extension"', }), - ]; diff --git a/utils/ModuleCache.js b/utils/ModuleCache.js index a06616de9b..4b1edc0eff 100644 --- a/utils/ModuleCache.js +++ b/utils/ModuleCache.js @@ -1,4 +1,5 @@ 'use strict'; + exports.__esModule = true; const log = require('debug')('eslint-module-utils:ModuleCache'); @@ -23,8 +24,10 @@ class ModuleCache { if (this.map.has(cacheKey)) { const f = this.map.get(cacheKey); // check freshness - if (process.hrtime(f.lastSeen)[0] < settings.lifetime) return f.result; - } else log('cache miss for', cacheKey); + if (process.hrtime(f.lastSeen)[0] < settings.lifetime) { return f.result; } + } else { + log('cache miss for', cacheKey); + } // cache miss return undefined; } diff --git a/utils/declaredScope.js b/utils/declaredScope.js index ded2131e49..dd2a20149f 100644 --- a/utils/declaredScope.js +++ b/utils/declaredScope.js @@ -1,9 +1,10 @@ 'use strict'; + exports.__esModule = true; exports.default = function declaredScope(context, name) { const references = context.getScope().references; - const reference = references.find(x => x.identifier.name === name); - if (!reference) return undefined; + const reference = references.find((x) => x.identifier.name === name); + if (!reference) { return undefined; } return reference.resolved.scope.type; }; diff --git a/utils/hash.js b/utils/hash.js index fcf00de38c..b9bff25bd9 100644 --- a/utils/hash.js +++ b/utils/hash.js @@ -2,7 +2,9 @@ * utilities for hashing config objects. * basically iteratively updates hash with a JSON-like format */ + 'use strict'; + exports.__esModule = true; const createHash = require('crypto').createHash; @@ -10,7 +12,7 @@ const createHash = require('crypto').createHash; const stringify = JSON.stringify; function hashify(value, hash) { - if (!hash) hash = createHash('sha256'); + if (!hash) { hash = createHash('sha256'); } if (Array.isArray(value)) { hashArray(value, hash); @@ -25,7 +27,7 @@ function hashify(value, hash) { exports.default = hashify; function hashArray(array, hash) { - if (!hash) hash = createHash('sha256'); + if (!hash) { hash = createHash('sha256'); } hash.update('['); for (let i = 0; i < array.length; i++) { @@ -40,10 +42,10 @@ hashify.array = hashArray; exports.hashArray = hashArray; function hashObject(object, hash) { - if (!hash) hash = createHash('sha256'); + if (!hash) { hash = createHash('sha256'); } hash.update('{'); - Object.keys(object).sort().forEach(key => { + Object.keys(object).sort().forEach((key) => { hash.update(stringify(key)); hash.update(':'); hashify(object[key], hash); @@ -56,4 +58,3 @@ function hashObject(object, hash) { hashify.object = hashObject; exports.hashObject = hashObject; - diff --git a/utils/ignore.js b/utils/ignore.js index 32bbbc6249..e41d1e5a50 100644 --- a/utils/ignore.js +++ b/utils/ignore.js @@ -1,4 +1,5 @@ 'use strict'; + exports.__esModule = true; const extname = require('path').extname; @@ -28,7 +29,7 @@ function makeValidExtensionSet(settings) { if (!Array.isArray(parserSettings)) { throw new TypeError('"settings" for ' + parser + ' must be an array'); } - parserSettings.forEach(ext => exts.add(ext)); + parserSettings.forEach((ext) => exts.add(ext)); } } @@ -38,9 +39,9 @@ exports.getFileExtensions = makeValidExtensionSet; exports.default = function ignore(path, context) { // check extension whitelist first (cheap) - if (!hasValidExtension(path, context)) return true; + if (!hasValidExtension(path, context)) { return true; } - if (!('import/ignore' in context.settings)) return false; + if (!('import/ignore' in context.settings)) { return false; } const ignoreStrings = context.settings['import/ignore']; for (let i = 0; i < ignoreStrings.length; i++) { diff --git a/utils/module-require.js b/utils/module-require.js index c03671ce5a..96ef82ba51 100644 --- a/utils/module-require.js +++ b/utils/module-require.js @@ -1,4 +1,5 @@ 'use strict'; + exports.__esModule = true; const Module = require('module'); diff --git a/utils/moduleVisitor.js b/utils/moduleVisitor.js index 4d93a0199b..c312ca2d45 100644 --- a/utils/moduleVisitor.js +++ b/utils/moduleVisitor.js @@ -1,4 +1,5 @@ 'use strict'; + exports.__esModule = true; /** @@ -16,14 +17,14 @@ exports.default = function visitModules(visitor, options) { let ignoreRegExps = []; if (options.ignore != null) { - ignoreRegExps = options.ignore.map(p => new RegExp(p)); + ignoreRegExps = options.ignore.map((p) => new RegExp(p)); } function checkSourceValue(source, importer) { - if (source == null) return; //? + if (source == null) { return; } //? // handle ignore - if (ignoreRegExps.some(re => re.test(source.value))) return; + if (ignoreRegExps.some((re) => re.test(source.value))) { return; } // fire visitor visitor(source, importer); @@ -41,14 +42,14 @@ exports.default = function visitModules(visitor, options) { if (node.type === 'ImportExpression') { modulePath = node.source; } else if (node.type === 'CallExpression') { - if (node.callee.type !== 'Import') return; - if (node.arguments.length !== 1) return; + if (node.callee.type !== 'Import') { return; } + if (node.arguments.length !== 1) { return; } modulePath = node.arguments[0]; } - if (modulePath.type !== 'Literal') return; - if (typeof modulePath.value !== 'string') return; + if (modulePath.type !== 'Literal') { return; } + if (typeof modulePath.value !== 'string') { return; } checkSourceValue(modulePath, node); } @@ -56,32 +57,35 @@ exports.default = function visitModules(visitor, options) { // for CommonJS `require` calls // adapted from @mctep: https://git.io/v4rAu function checkCommon(call) { - if (call.callee.type !== 'Identifier') return; - if (call.callee.name !== 'require') return; - if (call.arguments.length !== 1) return; + if (call.callee.type !== 'Identifier') { return; } + if (call.callee.name !== 'require') { return; } + if (call.arguments.length !== 1) { return; } const modulePath = call.arguments[0]; - if (modulePath.type !== 'Literal') return; - if (typeof modulePath.value !== 'string') return; + if (modulePath.type !== 'Literal') { return; } + if (typeof modulePath.value !== 'string') { return; } checkSourceValue(modulePath, call); } function checkAMD(call) { - if (call.callee.type !== 'Identifier') return; - if (call.callee.name !== 'require' && - call.callee.name !== 'define') return; - if (call.arguments.length !== 2) return; + if (call.callee.type !== 'Identifier') { return; } + if (call.callee.name !== 'require' && call.callee.name !== 'define') { return; } + if (call.arguments.length !== 2) { return; } const modules = call.arguments[0]; - if (modules.type !== 'ArrayExpression') return; + if (modules.type !== 'ArrayExpression') { return; } for (const element of modules.elements) { - if (element.type !== 'Literal') continue; - if (typeof element.value !== 'string') continue; + if (element.type !== 'Literal') { continue; } + if (typeof element.value !== 'string') { continue; } - if (element.value === 'require' || - element.value === 'exports') continue; // magic modules: https://github.com/requirejs/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#magic-modules + if ( + element.value === 'require' + || element.value === 'exports' + ) { + continue; // magic modules: https://github.com/requirejs/requirejs/wiki/Differences-between-the-simplified-CommonJS-wrapper-and-standard-AMD-define#magic-modules + } checkSourceValue(element, element); } @@ -90,20 +94,20 @@ exports.default = function visitModules(visitor, options) { const visitors = {}; if (options.esmodule) { Object.assign(visitors, { - 'ImportDeclaration': checkSource, - 'ExportNamedDeclaration': checkSource, - 'ExportAllDeclaration': checkSource, - 'CallExpression': checkImportCall, - 'ImportExpression': checkImportCall, + ImportDeclaration: checkSource, + ExportNamedDeclaration: checkSource, + ExportAllDeclaration: checkSource, + CallExpression: checkImportCall, + ImportExpression: checkImportCall, }); } if (options.commonjs || options.amd) { - const currentCallExpression = visitors['CallExpression']; - visitors['CallExpression'] = function (call) { - if (currentCallExpression) currentCallExpression(call); - if (options.commonjs) checkCommon(call); - if (options.amd) checkAMD(call); + const currentCallExpression = visitors.CallExpression; + visitors.CallExpression = function (call) { + if (currentCallExpression) { currentCallExpression(call); } + if (options.commonjs) { checkCommon(call); } + if (options.amd) { checkAMD(call); } }; } @@ -116,19 +120,19 @@ exports.default = function visitModules(visitor, options) { */ function makeOptionsSchema(additionalProperties) { const base = { - 'type': 'object', - 'properties': { - 'commonjs': { 'type': 'boolean' }, - 'amd': { 'type': 'boolean' }, - 'esmodule': { 'type': 'boolean' }, - 'ignore': { - 'type': 'array', - 'minItems': 1, - 'items': { 'type': 'string' }, - 'uniqueItems': true, + type: 'object', + properties: { + commonjs: { type: 'boolean' }, + amd: { type: 'boolean' }, + esmodule: { type: 'boolean' }, + ignore: { + type: 'array', + minItems: 1, + items: { type: 'string' }, + uniqueItems: true, }, }, - 'additionalProperties': false, + additionalProperties: false, }; if (additionalProperties) { diff --git a/utils/parse.js b/utils/parse.js index dd0746aaa7..7646b3177c 100644 --- a/utils/parse.js +++ b/utils/parse.js @@ -1,4 +1,5 @@ 'use strict'; + exports.__esModule = true; const moduleRequire = require('./module-require').default; @@ -23,10 +24,10 @@ function keysFromParser(parserPath, parserInstance, parsedResult) { if (parsedResult && parsedResult.visitorKeys) { return parsedResult.visitorKeys; } - if (typeof parserPath === 'string' && /.*espree.*/.test(parserPath)) { + if (typeof parserPath === 'string' && (/.*espree.*/).test(parserPath)) { return parserInstance.VisitorKeys; } - if (typeof parserPath === 'string' && /.*babel-eslint.*/.test(parserPath)) { + if (typeof parserPath === 'string' && (/.*babel-eslint.*/).test(parserPath)) { return getBabelEslintVisitorKeys(parserPath); } return null; @@ -51,13 +52,13 @@ function transformHashbang(text) { } exports.default = function parse(path, content, context) { - if (context == null) throw new Error('need context to parse properly'); + if (context == null) { throw new Error('need context to parse properly'); } // ESLint in "flat" mode only sets context.languageOptions.parserOptions - let parserOptions = (context.languageOptions && context.languageOptions.parserOptions) || context.parserOptions; + let parserOptions = context.languageOptions && context.languageOptions.parserOptions || context.parserOptions; const parserOrPath = getParser(path, context); - if (!parserOrPath) throw new Error('parserPath or languageOptions.parser is required!'); + if (!parserOrPath) { throw new Error('parserPath or languageOptions.parser is required!'); } // hack: espree blows up with frozen options parserOptions = Object.assign({}, parserOptions); @@ -103,9 +104,8 @@ exports.default = function parse(path, content, context) { } if (!ast || typeof ast !== 'object') { console.warn( - '`parseForESLint` from parser `' + - (typeof parserOrPath === 'string' ? parserOrPath : '`context.languageOptions.parser`') + // Can only be invalid for custom parser per imports/parser - '` is invalid and will just be ignored' + // Can only be invalid for custom parser per imports/parser + '`parseForESLint` from parser `' + (typeof parserOrPath === 'string' ? parserOrPath : '`context.languageOptions.parser`') + '` is invalid and will just be ignored' ); } else { return makeParseReturn(ast, keysFromParser(parserOrPath, parser, undefined)); diff --git a/utils/pkgUp.js b/utils/pkgUp.js index 049869719b..889f62265f 100644 --- a/utils/pkgUp.js +++ b/utils/pkgUp.js @@ -1,4 +1,5 @@ 'use strict'; + exports.__esModule = true; const fs = require('fs'); @@ -6,22 +7,22 @@ const path = require('path'); /** * Derived significantly from package find-up@2.0.0. See license below. - * + * * @copyright Sindre Sorhus * MIT License * * Copyright (c) Sindre Sorhus (https://sindresorhus.com) - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/utils/readPkgUp.js b/utils/readPkgUp.js index 6a6a1eea3e..d34fa6c818 100644 --- a/utils/readPkgUp.js +++ b/utils/readPkgUp.js @@ -1,4 +1,5 @@ 'use strict'; + exports.__esModule = true; const fs = require('fs'); @@ -10,22 +11,22 @@ function stripBOM(str) { /** * Derived significantly from read-pkg-up@2.0.0. See license below. - * + * * @copyright Sindre Sorhus * MIT License * * Copyright (c) Sindre Sorhus (https://sindresorhus.com) - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/utils/resolve.js b/utils/resolve.js index 9d9dfa8439..0ed5bdb0c9 100644 --- a/utils/resolve.js +++ b/utils/resolve.js @@ -1,4 +1,5 @@ 'use strict'; + exports.__esModule = true; const fs = require('fs'); @@ -53,16 +54,16 @@ function tryRequire(target, sourceFile) { // https://stackoverflow.com/a/27382838 exports.fileExistsWithCaseSync = function fileExistsWithCaseSync(filepath, cacheSettings, strict) { // don't care if the FS is case-sensitive - if (CASE_SENSITIVE_FS) return true; + if (CASE_SENSITIVE_FS) { return true; } // null means it resolved to a builtin - if (filepath === null) return true; - if (filepath.toLowerCase() === process.cwd().toLowerCase() && !strict) return true; + if (filepath === null) { return true; } + if (filepath.toLowerCase() === process.cwd().toLowerCase() && !strict) { return true; } const parsedPath = path.parse(filepath); const dir = parsedPath.dir; let result = fileExistsCache.get(filepath, cacheSettings); - if (result != null) return result; + if (result != null) { return result; } // base case if (dir === '' || parsedPath.root === filepath) { @@ -88,7 +89,7 @@ let memoizedHash = ''; function fullResolve(modulePath, sourceFile, settings) { // check if this is a bonus core module const coreSet = new Set(settings['import/core-modules']); - if (coreSet.has(modulePath)) return { found: true, path: null }; + if (coreSet.has(modulePath)) { return { found: true, path: null }; } const sourceDir = path.dirname(sourceFile); @@ -102,40 +103,28 @@ function fullResolve(modulePath, sourceFile, settings) { const cacheSettings = ModuleCache.getSettings(settings); const cachedPath = fileExistsCache.get(cacheKey, cacheSettings); - if (cachedPath !== undefined) return { found: true, path: cachedPath }; + if (cachedPath !== undefined) { return { found: true, path: cachedPath }; } function cache(resolvedPath) { fileExistsCache.set(cacheKey, resolvedPath); } function withResolver(resolver, config) { - - function v1() { - try { - const resolved = resolver.resolveImport(modulePath, sourceFile, config); - if (resolved === undefined) return { found: false }; - return { found: true, path: resolved }; - } catch (err) { - return { found: false }; - } - } - - function v2() { + if (resolver.interfaceVersion === 2) { return resolver.resolve(modulePath, sourceFile, config); } - switch (resolver.interfaceVersion) { - case 2: - return v2(); - - default: - case 1: - return v1(); + try { + const resolved = resolver.resolveImport(modulePath, sourceFile, config); + if (resolved === undefined) { return { found: false }; } + return { found: true, path: resolved }; + } catch (err) { + return { found: false }; } } - const configResolvers = (settings['import/resolver'] - || { 'node': settings['import/resolve'] }); // backward compatibility + const configResolvers = settings['import/resolver'] + || { node: settings['import/resolve'] }; // backward compatibility const resolvers = resolverReducer(configResolvers, new Map()); @@ -145,7 +134,7 @@ function fullResolve(modulePath, sourceFile, settings) { const resolver = requireResolver(name, sourceFile); const resolved = withResolver(resolver, config); - if (!resolved.found) continue; + if (!resolved.found) { continue; } // else, counts cache(resolved.path); @@ -160,7 +149,7 @@ exports.relative = relative; function resolverReducer(resolvers, map) { if (Array.isArray(resolvers)) { - resolvers.forEach(r => resolverReducer(r, map)); + resolvers.forEach((r) => resolverReducer(r, map)); return map; } @@ -186,9 +175,9 @@ function getBaseDir(sourceFile) { } function requireResolver(name, sourceFile) { // Try to resolve package with conventional name - const resolver = tryRequire(`eslint-import-resolver-${name}`, sourceFile) || - tryRequire(name, sourceFile) || - tryRequire(path.resolve(getBaseDir(sourceFile), name)); + const resolver = tryRequire(`eslint-import-resolver-${name}`, sourceFile) + || tryRequire(name, sourceFile) + || tryRequire(path.resolve(getBaseDir(sourceFile), name)); if (!resolver) { const err = new Error(`unable to load resolver "${name}".`); diff --git a/utils/unambiguous.js b/utils/unambiguous.js index 75f21693b7..24cb123157 100644 --- a/utils/unambiguous.js +++ b/utils/unambiguous.js @@ -1,4 +1,5 @@ 'use strict'; + exports.__esModule = true; const pattern = /(^|;)\s*(export|import)((\s+\w)|(\s*[{*=]))|import\(/m; @@ -25,5 +26,5 @@ const unambiguousNodeType = /^(?:(?:Exp|Imp)ort.*Declaration|TSExportAssignment) * @return {Boolean} */ exports.isModule = function isUnambiguousModule(ast) { - return ast.body && ast.body.some(node => unambiguousNodeType.test(node.type)); + return ast.body && ast.body.some((node) => unambiguousNodeType.test(node.type)); }; diff --git a/utils/visit.js b/utils/visit.js index 77b09850ae..6178faeaa0 100644 --- a/utils/visit.js +++ b/utils/visit.js @@ -1,4 +1,5 @@ 'use strict'; + exports.__esModule = true; exports.default = function visit(node, keys, visitorSpec) { From 7e9f651625ac6835ec73f21490ecd68eebac100b Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 13 Apr 2023 16:41:18 -0700 Subject: [PATCH 18/49] [resolvers] [*] [deps] update `is-core-module`, `resolve` --- resolvers/node/package.json | 4 ++-- resolvers/webpack/package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/resolvers/node/package.json b/resolvers/node/package.json index d13b48635f..c63ee976b6 100644 --- a/resolvers/node/package.json +++ b/resolvers/node/package.json @@ -30,8 +30,8 @@ "homepage": "https://github.com/import-js/eslint-plugin-import", "dependencies": { "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" + "is-core-module": "^2.12.0", + "resolve": "^1.22.2" }, "devDependencies": { "chai": "^3.5.0", diff --git a/resolvers/webpack/package.json b/resolvers/webpack/package.json index 42d5e7470e..ba21801e61 100644 --- a/resolvers/webpack/package.json +++ b/resolvers/webpack/package.json @@ -36,10 +36,10 @@ "find-root": "^1.1.0", "has": "^1.0.3", "interpret": "^1.4.0", - "is-core-module": "^2.11.0", + "is-core-module": "^2.12.0", "is-regex": "^1.1.4", "lodash": "^4.17.21", - "resolve": "^1.22.1", + "resolve": "^1.22.2", "semver": "^5.7.1" }, "peerDependencies": { From a89eadf3247af844da6b0f9e7bca7690777bf665 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 13 Apr 2023 16:44:24 -0700 Subject: [PATCH 19/49] [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 3733e2d76f..013422c71e 100644 --- a/package.json +++ b/package.json @@ -108,11 +108,11 @@ "eslint-import-resolver-node": "^0.3.7", "eslint-module-utils": "^2.7.4", "has": "^1.0.3", - "is-core-module": "^2.11.0", + "is-core-module": "^2.12.0", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.values": "^1.1.6", - "resolve": "^1.22.1", + "resolve": "^1.22.2", "semver": "^6.3.0", "tsconfig-paths": "^3.14.2" } From 1fa29717518732e3143aaf8155713133c383bf3c Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 14 Apr 2023 11:48:48 -0700 Subject: [PATCH 20/49] [Fix] `order`: partial fix for #2687 --- src/rules/order.js | 12 +++--- tests/src/core/importType.js | 4 ++ tests/src/rules/order.js | 73 +++++++++++++++++++++++++++++++++--- 3 files changed, 79 insertions(+), 10 deletions(-) diff --git a/src/rules/order.js b/src/rules/order.js index 5921989d2b..27c3f4b0f9 100644 --- a/src/rules/order.js +++ b/src/rules/order.js @@ -253,6 +253,7 @@ function makeOutOfOrderReport(context, imported) { if (!outOfOrder.length) { return; } + // There are things to report. Try to minimize the number of reported errors. const reversedImported = reverse(imported); const reversedOrder = findOutOfOrder(reversedImported); @@ -426,11 +427,12 @@ const types = ['builtin', 'external', 'internal', 'unknown', 'parent', 'sibling' // Example: { index: 0, sibling: 1, parent: 1, external: 1, builtin: 2, internal: 2 } // Will throw an error if it contains a type that does not exist, or has a duplicate function convertGroupsToRanks(groups) { + if (groups.length === 1) { + // TODO: remove this `if` and fix the bug + return convertGroupsToRanks(groups[0]); + } const rankObject = groups.reduce(function (res, group, index) { - if (typeof group === 'string') { - group = [group]; - } - group.forEach(function (groupItem) { + [].concat(group).forEach(function (groupItem) { if (types.indexOf(groupItem) === -1) { throw new Error(`Incorrect configuration of the rule: Unknown type \`${JSON.stringify(groupItem)}\``); } @@ -443,7 +445,7 @@ function convertGroupsToRanks(groups) { }, {}); const omittedTypes = types.filter(function (type) { - return rankObject[type] === undefined; + return typeof rankObject[type] === 'undefined'; }); const ranks = omittedTypes.reduce(function (res, type) { diff --git a/tests/src/core/importType.js b/tests/src/core/importType.js index 937f193033..bdc93d8a3e 100644 --- a/tests/src/core/importType.js +++ b/tests/src/core/importType.js @@ -17,7 +17,11 @@ describe('importType(name)', function () { it("should return 'builtin' for node.js modules", function () { expect(importType('fs', context)).to.equal('builtin'); + expect(importType('node:fs', context)).to.equal('builtin'); + expect(importType('fs/promises', context)).to.equal('builtin'); + expect(importType('node:fs/promises', context)).to.equal('builtin'); expect(importType('path', context)).to.equal('builtin'); + expect(importType('node:path', context)).to.equal('builtin'); }); it("should return 'external' for non-builtin modules without a relative path", function () { diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index 8d0b315e71..e7a9cb9c9b 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -3174,11 +3174,10 @@ context('TypeScript', function () { import type { ParsedPath } from 'path'; } `, - errors: [{ - message: '`fs` type import should occur before type import of `path`', - }, { - message: '`fs` type import should occur before type import of `path`', - }], + errors: [ + { message: '`fs` type import should occur before type import of `path`' }, + { message: '`fs` type import should occur before type import of `path`' }, + ], ...parserConfig, options: [ { @@ -3186,6 +3185,70 @@ context('TypeScript', function () { }, ], }), + + test({ + code: ` + import express from 'express'; + import log4js from 'log4js'; + import chpro from 'node:child_process'; + // import fsp from 'node:fs/promises'; + `, + output: ` + import chpro from 'node:child_process'; + import express from 'express'; + import log4js from 'log4js'; + // import fsp from 'node:fs/promises'; + `, + options: [{ + groups: [ + 'builtin', + 'external', + 'internal', + 'parent', + 'sibling', + 'index', + 'object', + 'type', + ], + }], + errors: [ + { message: '`node:child_process` import should occur before import of `express`' }, + // { message: '`node:fs/promises` import should occur before import of `express`' }, + ], + }), + + test({ + code: ` + import express from 'express'; + import log4js from 'log4js'; + import chpro from 'node:child_process'; + // import fsp from 'node:fs/promises'; + `, + output: ` + import chpro from 'node:child_process'; + import express from 'express'; + import log4js from 'log4js'; + // import fsp from 'node:fs/promises'; + `, + options: [{ + groups: [ + [ + 'builtin', + 'external', + 'internal', + 'parent', + 'sibling', + 'index', + 'object', + 'type', + ], + ], + }], + errors: [ + { message: '`node:child_process` import should occur before import of `express`' }, + // { message: '`node:fs/promises` import should occur before import of `express`' }, + ], + }), ], }); }); From ef094f2031dcb219e3aae1a9b508d8d49fd2489f Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 14 Apr 2023 11:50:25 -0700 Subject: [PATCH 21/49] [meta] add missing CHANGELOG entry --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe7f6771b8..8a8ec36692 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [`consistent-type-specifier-style`]: fix accidental removal of comma in certain cases ([#2754], thanks [@bradzacher]) - [Perf] `ExportMap`: Improve `ExportMap.for` performance on larger codebases ([#2756], thanks [@leipert]) - [`no-extraneous-dependencies`]/TypeScript: do not error when importing inline type from dev dependencies ([#1820], thanks [@andyogo]) +* [`order`]: partial fix for [#2687] (thanks [@ljharb]) ### Changed - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) @@ -1397,6 +1398,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 +[#2687]: https://github.com/import-js/eslint-plugin-import/issues/2687 [#2684]: https://github.com/import-js/eslint-plugin-import/issues/2684 [#2674]: https://github.com/import-js/eslint-plugin-import/issues/2674 [#2668]: https://github.com/import-js/eslint-plugin-import/issues/2668 From 261ee3aa7666edb0131e08db3d3d3428b013df42 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 14 Apr 2023 11:51:58 -0700 Subject: [PATCH 22/49] [Deps] update `resolve` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 013422c71e..a0fdc66100 100644 --- a/package.json +++ b/package.json @@ -112,7 +112,7 @@ "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.values": "^1.1.6", - "resolve": "^1.22.2", + "resolve": "^1.22.3", "semver": "^6.3.0", "tsconfig-paths": "^3.14.2" } From 4c6fa3e1e47198eb448690c1c5ef486c162e6c00 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 14 Apr 2023 11:58:36 -0700 Subject: [PATCH 23/49] [Tests] fix tests for older nodes --- tests/src/core/importType.js | 13 ++-- tests/src/rules/order.js | 113 ++++++++++++++++++----------------- 2 files changed, 65 insertions(+), 61 deletions(-) diff --git a/tests/src/core/importType.js b/tests/src/core/importType.js index bdc93d8a3e..c4dca866e2 100644 --- a/tests/src/core/importType.js +++ b/tests/src/core/importType.js @@ -1,5 +1,6 @@ import { expect } from 'chai'; import * as path from 'path'; +import isCoreModule from 'is-core-module'; import importType, { isExternalModule, isScoped, isAbsolute } from 'core/importType'; @@ -16,12 +17,12 @@ describe('importType(name)', function () { }); it("should return 'builtin' for node.js modules", function () { - expect(importType('fs', context)).to.equal('builtin'); - expect(importType('node:fs', context)).to.equal('builtin'); - expect(importType('fs/promises', context)).to.equal('builtin'); - expect(importType('node:fs/promises', context)).to.equal('builtin'); - expect(importType('path', context)).to.equal('builtin'); - expect(importType('node:path', context)).to.equal('builtin'); + ['fs', 'fs/promises', 'path'].filter((x) => isCoreModule(x)).forEach((x) => { + expect(importType(x, context)).to.equal('builtin'); + if (isCoreModule(`node:${x}`)) { + expect(importType(`node:${x}`, context)).to.equal('builtin'); + } + }); }); it("should return 'external' for non-builtin modules without a relative path", function () { diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index e7a9cb9c9b..84b341e1b0 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -5,6 +5,7 @@ import eslintPkg from 'eslint/package.json'; import semver from 'semver'; import flatMap from 'array.prototype.flatmap'; import { resolve } from 'path'; +import isCoreModule from 'is-core-module'; import { default as babelPresetFlow } from 'babel-preset-flow'; const ruleTester = new RuleTester(); @@ -2962,7 +2963,7 @@ context('TypeScript', function () { ], }), ], - invalid: [ + invalid: [].concat( // Option alphabetize: {order: 'asc'} test({ code: ` @@ -3186,53 +3187,22 @@ context('TypeScript', function () { ], }), - test({ - code: ` - import express from 'express'; - import log4js from 'log4js'; - import chpro from 'node:child_process'; - // import fsp from 'node:fs/promises'; - `, - output: ` - import chpro from 'node:child_process'; - import express from 'express'; - import log4js from 'log4js'; - // import fsp from 'node:fs/promises'; - `, - options: [{ - groups: [ - 'builtin', - 'external', - 'internal', - 'parent', - 'sibling', - 'index', - 'object', - 'type', - ], - }], - errors: [ - { message: '`node:child_process` import should occur before import of `express`' }, - // { message: '`node:fs/promises` import should occur before import of `express`' }, - ], - }), - - test({ - code: ` - import express from 'express'; - import log4js from 'log4js'; - import chpro from 'node:child_process'; - // import fsp from 'node:fs/promises'; - `, - output: ` - import chpro from 'node:child_process'; - import express from 'express'; - import log4js from 'log4js'; - // import fsp from 'node:fs/promises'; - `, - options: [{ - groups: [ - [ + isCoreModule('node:child_process') && isCoreModule('node:fs/promises') ? [ + test({ + code: ` + import express from 'express'; + import log4js from 'log4js'; + import chpro from 'node:child_process'; + // import fsp from 'node:fs/promises'; + `, + output: ` + import chpro from 'node:child_process'; + import express from 'express'; + import log4js from 'log4js'; + // import fsp from 'node:fs/promises'; + `, + options: [{ + groups: [ 'builtin', 'external', 'internal', @@ -3242,14 +3212,47 @@ context('TypeScript', function () { 'object', 'type', ], + }], + errors: [ + { message: '`node:child_process` import should occur before import of `express`' }, + // { message: '`node:fs/promises` import should occur before import of `express`' }, ], - }], - errors: [ - { message: '`node:child_process` import should occur before import of `express`' }, - // { message: '`node:fs/promises` import should occur before import of `express`' }, - ], - }), - ], + }), + + test({ + code: ` + import express from 'express'; + import log4js from 'log4js'; + import chpro from 'node:child_process'; + // import fsp from 'node:fs/promises'; + `, + output: ` + import chpro from 'node:child_process'; + import express from 'express'; + import log4js from 'log4js'; + // import fsp from 'node:fs/promises'; + `, + options: [{ + groups: [ + [ + 'builtin', + 'external', + 'internal', + 'parent', + 'sibling', + 'index', + 'object', + 'type', + ], + ], + }], + errors: [ + { message: '`node:child_process` import should occur before import of `express`' }, + // { message: '`node:fs/promises` import should occur before import of `express`' }, + ], + }), + ] : [], + ), }); }); }); From 463ce199da3002cbf49d56917085bed94b5a43de Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 14 Apr 2023 12:03:43 -0700 Subject: [PATCH 24/49] [utils] v2.8.0 --- utils/CHANGELOG.md | 9 ++++++++- utils/package.json | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/utils/CHANGELOG.md b/utils/CHANGELOG.md index 7d2057dec2..ae3588a390 100644 --- a/utils/CHANGELOG.md +++ b/utils/CHANGELOG.md @@ -5,6 +5,11 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## Unreleased +## v2.8.0 - 2023-04-14 + +### New +- `parse`: support flat config ([#2714], thanks [@DMartens]) + ### Fixed - Improve performance of `fullResolve` for large projects ([#2755], thanks [@leipert]) @@ -19,7 +24,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## v2.7.3 - 2022-01-26 ### Fixed -- [Fix] `parse`: restore compatibility by making the return value `ast` again ([#2350], thanks [@ljharb]) +- `parse`: restore compatibility by making the return value `ast` again ([#2350], thanks [@ljharb]) ## v2.7.2 - 2022-01-01 @@ -127,6 +132,7 @@ Yanked due to critical issue with cache key resulting from #839. - `unambiguous.test()` regex is now properly in multiline mode [#2755]: https://github.com/import-js/eslint-plugin-import/pull/2755 +[#2714]: https://github.com/import-js/eslint-plugin-import/pull/2714 [#2523]: https://github.com/import-js/eslint-plugin-import/pull/2523 [#2431]: https://github.com/import-js/eslint-plugin-import/pull/2431 [#2350]: https://github.com/import-js/eslint-plugin-import/issues/2350 @@ -159,6 +165,7 @@ Yanked due to critical issue with cache key resulting from #839. [@bradzacher]: https://github.com/bradzacher [@brettz9]: https://github.com/brettz9 [@christophercurrie]: https://github.com/christophercurrie +[@DMartens]: https://github.com/DMartens [@hulkish]: https://github.com/hulkish [@Hypnosphi]: https://github.com/Hypnosphi [@iamnapo]: https://github.com/iamnapo diff --git a/utils/package.json b/utils/package.json index 0c0678a5ec..d56c442b1a 100644 --- a/utils/package.json +++ b/utils/package.json @@ -1,6 +1,6 @@ { "name": "eslint-module-utils", - "version": "2.7.4", + "version": "2.8.0", "description": "Core utilities to support eslint-plugin-import and other module-related plugins.", "engines": { "node": ">=4" From 683d3a5927adf97a1654cfb4d9d3caf148e1eb8a Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 14 Apr 2023 13:02:33 -0700 Subject: [PATCH 25/49] [Deps] update `eslint-module-utils` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a0fdc66100..59ab24c57b 100644 --- a/package.json +++ b/package.json @@ -106,7 +106,7 @@ "debug": "^3.2.7", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", + "eslint-module-utils": "^2.8.0", "has": "^1.0.3", "is-core-module": "^2.12.0", "is-glob": "^4.0.3", From eaa1591c47679b3523696bca346d997b9598b508 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 17 Apr 2023 13:58:40 -0700 Subject: [PATCH 26/49] [Tests] switch some files to unix line endings --- tests/files/foo-bar-resolver-no-version.js | 24 +++++++++---------- tests/files/foo-bar-resolver-v1.js | 28 +++++++++++----------- tests/files/foo-bar-resolver-v2.js | 28 +++++++++++----------- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/tests/files/foo-bar-resolver-no-version.js b/tests/files/foo-bar-resolver-no-version.js index f00198562e..2a2d451850 100644 --- a/tests/files/foo-bar-resolver-no-version.js +++ b/tests/files/foo-bar-resolver-no-version.js @@ -1,12 +1,12 @@ -var path = require('path') - -exports.resolveImport = function (modulePath, sourceFile, config) { - var sourceFileName = path.basename(sourceFile) - if (sourceFileName === 'foo.js') { - return path.join(__dirname, 'bar.jsx') - } - if (sourceFileName === 'exception.js') { - throw new Error('foo-bar-resolver-v1 resolveImport test exception') - } - return undefined; -} +var path = require('path') + +exports.resolveImport = function (modulePath, sourceFile, config) { + var sourceFileName = path.basename(sourceFile) + if (sourceFileName === 'foo.js') { + return path.join(__dirname, 'bar.jsx') + } + if (sourceFileName === 'exception.js') { + throw new Error('foo-bar-resolver-v1 resolveImport test exception') + } + return undefined; +} diff --git a/tests/files/foo-bar-resolver-v1.js b/tests/files/foo-bar-resolver-v1.js index af9da1b7a6..7ba97cb55f 100644 --- a/tests/files/foo-bar-resolver-v1.js +++ b/tests/files/foo-bar-resolver-v1.js @@ -1,14 +1,14 @@ -var path = require('path') - -exports.resolveImport = function (modulePath, sourceFile, config) { - var sourceFileName = path.basename(sourceFile) - if (sourceFileName === 'foo.js') { - return path.join(__dirname, 'bar.jsx'); - } - if (sourceFileName === 'exception.js') { - throw new Error('foo-bar-resolver-v1 resolveImport test exception'); - } - return undefined; -}; - -exports.interfaceVersion = 1; +var path = require('path') + +exports.resolveImport = function (modulePath, sourceFile, config) { + var sourceFileName = path.basename(sourceFile) + if (sourceFileName === 'foo.js') { + return path.join(__dirname, 'bar.jsx'); + } + if (sourceFileName === 'exception.js') { + throw new Error('foo-bar-resolver-v1 resolveImport test exception'); + } + return undefined; +}; + +exports.interfaceVersion = 1; diff --git a/tests/files/foo-bar-resolver-v2.js b/tests/files/foo-bar-resolver-v2.js index 7f8bcc0f86..13135e3925 100644 --- a/tests/files/foo-bar-resolver-v2.js +++ b/tests/files/foo-bar-resolver-v2.js @@ -1,14 +1,14 @@ -var path = require('path') - -exports.resolve = function (modulePath, sourceFile, config) { - var sourceFileName = path.basename(sourceFile) - if (sourceFileName === 'foo.js') { - return { found: true, path: path.join(__dirname, 'bar.jsx') } - } - if (sourceFileName === 'exception.js') { - throw new Error('foo-bar-resolver-v2 resolve test exception') - } - return { found: false }; -}; - -exports.interfaceVersion = 2; +var path = require('path') + +exports.resolve = function (modulePath, sourceFile, config) { + var sourceFileName = path.basename(sourceFile) + if (sourceFileName === 'foo.js') { + return { found: true, path: path.join(__dirname, 'bar.jsx') } + } + if (sourceFileName === 'exception.js') { + throw new Error('foo-bar-resolver-v2 resolve test exception') + } + return { found: false }; +}; + +exports.interfaceVersion = 2; From afaefbbda83ed7b637ed4a039be6ad19727ea244 Mon Sep 17 00:00:00 2001 From: JounQin Date: Sun, 7 Aug 2022 12:29:33 +0800 Subject: [PATCH 27/49] [Refactor] `ExportMap`: rename `tsConfig` to `tsconfig` --- src/ExportMap.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ExportMap.js b/src/ExportMap.js index cd5bad56c3..f61d3c170a 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -24,7 +24,7 @@ let ts; const log = debug('eslint-plugin-import:ExportMap'); const exportCache = new Map(); -const tsConfigCache = new Map(); +const tsconfigCache = new Map(); export default class ExportMap { constructor(path) { @@ -549,20 +549,20 @@ ExportMap.parse = function (path, content, context) { const source = makeSourceCode(content, ast); function readTsConfig(context) { - const tsConfigInfo = tsConfigLoader({ + const tsconfigInfo = tsConfigLoader({ cwd: context.parserOptions && context.parserOptions.tsconfigRootDir || process.cwd(), getEnv: (key) => process.env[key], }); try { - if (tsConfigInfo.tsConfigPath !== undefined) { + if (tsconfigInfo.tsConfigPath !== undefined) { // Projects not using TypeScript won't have `typescript` installed. if (!ts) { ts = require('typescript'); } // eslint-disable-line import/no-extraneous-dependencies - const configFile = ts.readConfigFile(tsConfigInfo.tsConfigPath, ts.sys.readFile); + const configFile = ts.readConfigFile(tsconfigInfo.tsConfigPath, ts.sys.readFile); return ts.parseJsonConfigFileContent( configFile.config, ts.sys, - dirname(tsConfigInfo.tsConfigPath), + dirname(tsconfigInfo.tsConfigPath), ); } } catch (e) { @@ -576,10 +576,10 @@ ExportMap.parse = function (path, content, context) { const cacheKey = hashObject({ tsconfigRootDir: context.parserOptions && context.parserOptions.tsconfigRootDir, }).digest('hex'); - let tsConfig = tsConfigCache.get(cacheKey); + let tsConfig = tsconfigCache.get(cacheKey); if (typeof tsConfig === 'undefined') { tsConfig = readTsConfig(context); - tsConfigCache.set(cacheKey, tsConfig); + tsconfigCache.set(cacheKey, tsConfig); } return tsConfig && tsConfig.options ? tsConfig.options.esModuleInterop : false; From 328064abc707d3289772a8a29da5783c6dc345f6 Mon Sep 17 00:00:00 2001 From: silverwind Date: Tue, 18 Apr 2023 20:03:39 +0200 Subject: [PATCH 28/49] Fix invalid YAML in import/parsers example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c6f1a3211..ca99b8fafb 100644 --- a/README.md +++ b/README.md @@ -355,7 +355,7 @@ directly using webpack, for example: # .eslintrc.yml settings: import/parsers: - @typescript-eslint/parser: [ .ts, .tsx ] + "@typescript-eslint/parser": [ .ts, .tsx ] ``` In this case, [`@typescript-eslint/parser`](https://www.npmjs.com/package/@typescript-eslint/parser) From 88dd8153571373151c3c7b508c5944cb2bba1588 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 28 May 2023 09:02:13 -0800 Subject: [PATCH 29/49] [Deps] update `is-core-module` --- package.json | 2 +- src/rules/default.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index 59ab24c57b..feee6e46de 100644 --- a/package.json +++ b/package.json @@ -108,7 +108,7 @@ "eslint-import-resolver-node": "^0.3.7", "eslint-module-utils": "^2.8.0", "has": "^1.0.3", - "is-core-module": "^2.12.0", + "is-core-module": "^2.12.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.values": "^1.1.6", diff --git a/src/rules/default.js b/src/rules/default.js index f6b786020d..297a80c463 100644 --- a/src/rules/default.js +++ b/src/rules/default.js @@ -13,9 +13,7 @@ module.exports = { }, create(context) { - function checkDefault(specifierType, node) { - const defaultSpecifier = node.specifiers.find( (specifier) => specifier.type === specifierType, ); From a24a03b5ab4a257c78e5e0742b08023a002645e0 Mon Sep 17 00:00:00 2001 From: Kirill Korolyov Date: Mon, 3 Jul 2023 21:18:16 +0100 Subject: [PATCH 30/49] [meta] Add "eslint-plugin" to the list of keywords in package.json --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index feee6e46de..37911c90d8 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "keywords": [ "eslint", "eslintplugin", + "eslint-plugin", "es6", "jsnext", "modules", From 66e755fb32d900517df97efcd7707561a53dfc99 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 3 Jul 2023 22:37:00 -0500 Subject: [PATCH 31/49] [Refactor] `exports-last`: use `array.prototype.findlastindex` --- CHANGELOG.md | 1 + package.json | 1 + src/rules/exports-last.js | 11 ++++------- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a8ec36692..2c0fe3fdf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) - [Docs] [`group-exports`]: fix syntax highlighting ([#2699], thanks [@devinrhode2]) - [Docs] [`extensions`]: reference node ESM behavior ([#2748], thanks [@xM8WVqaG]) +- [Refactor] [`exports-last`]: use `array.prototype.findlastindex` (thanks [@ljharb]) ## [2.27.5] - 2023-01-16 diff --git a/package.json b/package.json index 37911c90d8..9127841b3d 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ }, "dependencies": { "array-includes": "^3.1.6", + "array.prototype.findlastindex": "^1.2.2", "array.prototype.flat": "^1.3.1", "array.prototype.flatmap": "^1.3.1", "debug": "^3.2.7", diff --git a/src/rules/exports-last.js b/src/rules/exports-last.js index c4ed97e22f..1e79f8339c 100644 --- a/src/rules/exports-last.js +++ b/src/rules/exports-last.js @@ -1,3 +1,5 @@ +import findLastIndex from 'array.prototype.findlastindex'; + import docsUrl from '../docsUrl'; function isNonExportStatement({ type }) { @@ -20,15 +22,10 @@ module.exports = { create(context) { return { Program({ body }) { - const lastNonExportStatementIndex = body.reduce(function findLastIndex(acc, item, index) { - if (isNonExportStatement(item)) { - return index; - } - return acc; - }, -1); + const lastNonExportStatementIndex = findLastIndex(body, isNonExportStatement); if (lastNonExportStatementIndex !== -1) { - body.slice(0, lastNonExportStatementIndex).forEach(function checkNonExport(node) { + body.slice(0, lastNonExportStatementIndex).forEach((node) => { if (!isNonExportStatement(node)) { context.report({ node, From 2c196b097f7812992c5987c45ce53e33a66a8021 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 3 Jul 2023 22:44:09 -0500 Subject: [PATCH 32/49] [Refactor] `no-anonymous-default-export`: use `object.fromentries` --- CHANGELOG.md | 1 + package.json | 1 + src/rules/no-anonymous-default-export.js | 12 +++++------- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c0fe3fdf0..fdc41210b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [Docs] [`group-exports`]: fix syntax highlighting ([#2699], thanks [@devinrhode2]) - [Docs] [`extensions`]: reference node ESM behavior ([#2748], thanks [@xM8WVqaG]) - [Refactor] [`exports-last`]: use `array.prototype.findlastindex` (thanks [@ljharb]) +- [Refactor] [`no-anonymous-default-export`]: use `object.fromentries` (thanks [@ljharb]) ## [2.27.5] - 2023-01-16 diff --git a/package.json b/package.json index 9127841b3d..763b5efc0c 100644 --- a/package.json +++ b/package.json @@ -113,6 +113,7 @@ "is-core-module": "^2.12.1", "is-glob": "^4.0.3", "minimatch": "^3.1.2", + "object.fromentries": "^2.0.6", "object.values": "^1.1.6", "resolve": "^1.22.3", "semver": "^6.3.0", diff --git a/src/rules/no-anonymous-default-export.js b/src/rules/no-anonymous-default-export.js index 80950d550e..06ea854b33 100644 --- a/src/rules/no-anonymous-default-export.js +++ b/src/rules/no-anonymous-default-export.js @@ -3,8 +3,11 @@ * @author Duncan Beevers */ -import docsUrl from '../docsUrl'; import has from 'has'; +import values from 'object.values'; +import fromEntries from 'object.fromentries'; + +import docsUrl from '../docsUrl'; const defs = { ArrayExpression: { @@ -68,12 +71,7 @@ const schemaProperties = Object.keys(defs) return acc; }, {}); -const defaults = Object.keys(defs) - .map((key) => defs[key]) - .reduce((acc, def) => { - acc[def.option] = has(def, 'default') ? def.default : false; - return acc; - }, {}); +const defaults = fromEntries(values(defs).map((def) => [def.option, has(def, 'default') ? def.default : false])); module.exports = { meta: { From 3a5ad34ca69a5c3239fff56241eb7e353d87274c Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 3 Jul 2023 22:48:40 -0500 Subject: [PATCH 33/49] [Refactor] `no-unused-modules`: use `array.prototype.flatmap` --- CHANGELOG.md | 1 + src/rules/no-unused-modules.js | 24 ++++++++++-------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fdc41210b4..68d7156784 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [Docs] [`extensions`]: reference node ESM behavior ([#2748], thanks [@xM8WVqaG]) - [Refactor] [`exports-last`]: use `array.prototype.findlastindex` (thanks [@ljharb]) - [Refactor] [`no-anonymous-default-export`]: use `object.fromentries` (thanks [@ljharb]) +- [Refactor] [`no-unused-modules`]: use `array.prototype.flatmap` (thanks [@ljharb]) ## [2.27.5] - 2023-01-16 diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index 4b09128a10..a2b6f4ea27 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -4,15 +4,17 @@ * @author René Fermann */ -import Exports, { recursivePatternCapture } from '../ExportMap'; import { getFileExtensions } from 'eslint-module-utils/ignore'; import resolve from 'eslint-module-utils/resolve'; import visit from 'eslint-module-utils/visit'; -import docsUrl from '../docsUrl'; import { dirname, join } from 'path'; import readPkgUp from 'eslint-module-utils/readPkgUp'; import values from 'object.values'; import includes from 'array-includes'; +import flatMap from 'array.prototype.flatmap'; + +import Exports, { recursivePatternCapture } from '../ExportMap'; +import docsUrl from '../docsUrl'; let FileEnumerator; let listFilesToProcess; @@ -40,12 +42,7 @@ try { const { listFilesToProcess: originalListFilesToProcess } = require('eslint/lib/util/glob-util'); listFilesToProcess = function (src, extensions) { - const patterns = src.reduce( - (carry, pattern) => carry.concat( - extensions.map((extension) => (/\*\*|\*\./).test(pattern) ? pattern : `${pattern}/**/*${extension}`), - ), - src, - ); + const patterns = src.concat(flatMap(src, (pattern) => extensions.map((extension) => (/\*\*|\*\./).test(pattern) ? pattern : `${pattern}/**/*${extension}`))); return originalListFilesToProcess(patterns); }; @@ -171,18 +168,17 @@ const isNodeModule = (path) => (/\/(node_modules)\//).test(path); const resolveFiles = (src, ignoreExports, context) => { const extensions = Array.from(getFileExtensions(context.settings)); - const srcFiles = new Set(); const srcFileList = listFilesToProcess(src, extensions); // prepare list of ignored files - const ignoredFilesList = listFilesToProcess(ignoreExports, extensions); + const ignoredFilesList = listFilesToProcess(ignoreExports, extensions); ignoredFilesList.forEach(({ filename }) => ignoredFiles.add(filename)); // prepare list of source files, don't consider files from node_modules - srcFileList.filter(({ filename }) => !isNodeModule(filename)).forEach(({ filename }) => { - srcFiles.add(filename); - }); - return srcFiles; + + return new Set( + srcFileList.filter(({ filename }) => !isNodeModule(filename)).map(({ filename }) => filename), + ); }; /** From ee00a1cc702cdfccbb2f10cddcb2797827115974 Mon Sep 17 00:00:00 2001 From: laurens-dg Date: Tue, 18 Jul 2023 15:35:15 +0200 Subject: [PATCH 34/49] [Fix] guard against empty parent --- CHANGELOG.md | 3 +++ src/rules/newline-after-import.js | 5 +++++ tests/src/rules/newline-after-import.js | 16 +++++++++++++--- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68d7156784..dcfb192d2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [`consistent-type-specifier-style`]: fix accidental removal of comma in certain cases ([#2754], thanks [@bradzacher]) - [Perf] `ExportMap`: Improve `ExportMap.for` performance on larger codebases ([#2756], thanks [@leipert]) - [`no-extraneous-dependencies`]/TypeScript: do not error when importing inline type from dev dependencies ([#1820], thanks [@andyogo]) +- [`newline-after-import`]/TypeScript: do not error when re-exporting a namespaced import ([#2832], thanks [@laurens-dg]) * [`order`]: partial fix for [#2687] (thanks [@ljharb]) ### Changed @@ -1072,6 +1073,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2832]: https://github.com/import-js/eslint-plugin-import/pull/2832 [#2756]: https://github.com/import-js/eslint-plugin-import/pull/2756 [#2754]: https://github.com/import-js/eslint-plugin-import/pull/2754 [#2748]: https://github.com/import-js/eslint-plugin-import/pull/2748 @@ -1746,6 +1748,7 @@ for info on changes for earlier releases. [@KostyaZgara]: https://github.com/KostyaZgara [@kylemh]: https://github.com/kylemh [@laysent]: https://github.com/laysent +[@laurens-dg]: https://github.com/laurens-dg [@le0nik]: https://github.com/le0nik [@leipert]: https://github.com/leipert [@lemonmade]: https://github.com/lemonmade diff --git a/src/rules/newline-after-import.js b/src/rules/newline-after-import.js index c63bb21b24..8855e26e52 100644 --- a/src/rules/newline-after-import.js +++ b/src/rules/newline-after-import.js @@ -149,6 +149,11 @@ module.exports = { function checkImport(node) { const { parent } = node; + + if (!parent || !parent.body) { + return; + } + const nodePosition = parent.body.indexOf(node); const nextNode = parent.body[nodePosition + 1]; const endLine = node.loc.end.line; diff --git a/tests/src/rules/newline-after-import.js b/tests/src/rules/newline-after-import.js index 5e14b570ee..9ec6eb757b 100644 --- a/tests/src/rules/newline-after-import.js +++ b/tests/src/rules/newline-after-import.js @@ -26,7 +26,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { code: ` const x = () => require('baz') , y = () => require('bar') - + // some comment here `, parserOptions: { ecmaVersion: 6 }, @@ -273,6 +273,16 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { parser, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, + { + code: ` + import { ns } from 'namespace'; + import Bar = ns.baz.foo.Bar; + + export import Foo = ns.baz.bar.Foo; + `, + parser, + parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, + }, )), { code: ` @@ -299,7 +309,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { { code: ` import path from 'path';import foo from 'foo'; - + /** * some multiline comment here * another line of comment @@ -313,7 +323,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { code: ` import path from 'path'; import foo from 'foo'; - + // Some random single line comment var bar = 42; `, From e2cf99c21130404fbf68394434b8cfa6d88a1bd0 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 24 Jul 2023 11:34:22 -0700 Subject: [PATCH 35/49] [Deps] update `semver` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 763b5efc0c..e870082e34 100644 --- a/package.json +++ b/package.json @@ -116,7 +116,7 @@ "object.fromentries": "^2.0.6", "object.values": "^1.1.6", "resolve": "^1.22.3", - "semver": "^6.3.0", + "semver": "^6.3.1", "tsconfig-paths": "^3.14.2" } } From f302f7d31d28c91bd483c5da14600ce6e26cd0e3 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 18 Jul 2023 21:46:55 -0400 Subject: [PATCH 36/49] [Fix] `no-duplicates`: Prefer combined type and regular imports when using `prefer-inline` --- CHANGELOG.md | 3 +++ docs/rules/no-duplicates.md | 5 +++++ src/rules/no-duplicates.js | 5 +++-- tests/src/rules/no-duplicates.js | 19 +++++++++++++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcfb192d2a..a46b1b0326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [`no-extraneous-dependencies`]/TypeScript: do not error when importing inline type from dev dependencies ([#1820], thanks [@andyogo]) - [`newline-after-import`]/TypeScript: do not error when re-exporting a namespaced import ([#2832], thanks [@laurens-dg]) * [`order`]: partial fix for [#2687] (thanks [@ljharb]) +- [`no-duplicates`]: Detect across type and regular imports ([#2835], thanks [@benkrejci]) ### Changed - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) @@ -1073,6 +1074,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2835]: https://github.com/import-js/eslint-plugin-import/pull/2835 [#2832]: https://github.com/import-js/eslint-plugin-import/pull/2832 [#2756]: https://github.com/import-js/eslint-plugin-import/pull/2756 [#2754]: https://github.com/import-js/eslint-plugin-import/pull/2754 @@ -1647,6 +1649,7 @@ for info on changes for earlier releases. [@BarryThePenguin]: https://github.com/BarryThePenguin [@be5invis]: https://github.com/be5invis [@beatrizrezener]: https://github.com/beatrizrezener +[@benkrejci]: https://github.com/benkrejci [@benmosher]: https://github.com/benmosher [@benmunro]: https://github.com/benmunro [@BenoitZugmeyer]: https://github.com/BenoitZugmeyer diff --git a/docs/rules/no-duplicates.md b/docs/rules/no-duplicates.md index 5f3cfbd426..dd741c21a5 100644 --- a/docs/rules/no-duplicates.md +++ b/docs/rules/no-duplicates.md @@ -84,12 +84,17 @@ Config: ```js import { AValue, type AType } from './mama-mia' import type { BType } from './mama-mia' + +import { CValue } from './papa-mia' +import type { CType } from './papa-mia' ``` ✅ Valid with `["error", {"prefer-inline": true}]` ```js import { AValue, type AType, type BType } from './mama-mia' + +import { CValue, type CType } from './papa-mia' ``` diff --git a/src/rules/no-duplicates.js b/src/rules/no-duplicates.js index 76bf187b2e..2373202cb6 100644 --- a/src/rules/no-duplicates.js +++ b/src/rules/no-duplicates.js @@ -318,10 +318,11 @@ module.exports = { }); } const map = moduleMaps.get(n.parent); - if (n.importKind === 'type') { + const preferInline = context.options[0] && context.options[0]['prefer-inline']; + if (!preferInline && n.importKind === 'type') { return n.specifiers.length > 0 && n.specifiers[0].type === 'ImportDefaultSpecifier' ? map.defaultTypesImported : map.namedTypesImported; } - if (n.specifiers.some((spec) => spec.importKind === 'type')) { + if (!preferInline && n.specifiers.some((spec) => spec.importKind === 'type')) { return map.namedTypesImported; } diff --git a/tests/src/rules/no-duplicates.js b/tests/src/rules/no-duplicates.js index 33e1e632e2..d61fda86e1 100644 --- a/tests/src/rules/no-duplicates.js +++ b/tests/src/rules/no-duplicates.js @@ -711,6 +711,25 @@ context('TypeScript', function () { }, ], }), + // #2834 Detect duplicates across type and regular imports + test({ + code: "import {AValue} from './foo'; import type {AType} from './foo'", + ...parserConfig, + options: [{ 'prefer-inline': true }], + output: `import {AValue,type AType} from './foo'; `, + errors: [ + { + line: 1, + column: 22, + message: "'./foo' imported multiple times.", + }, + { + line: 1, + column: 56, + message: "'./foo' imported multiple times.", + }, + ], + }), ]); ruleTester.run('no-duplicates', rule, { From d8002bee587dad9c574fb06a23b4bcb25e90bc39 Mon Sep 17 00:00:00 2001 From: Ben Asher Date: Sun, 7 May 2023 18:57:33 -0700 Subject: [PATCH 37/49] [Fix] `extensions`: handle `.` and `..` properly --- CHANGELOG.md | 5 ++- src/rules/extensions.js | 1 + tests/files/internal-modules/test.js | 0 tests/index.js | 1 + tests/src/rules/extensions.js | 49 ++++++++++++++++++++++++++++ 5 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 tests/files/internal-modules/test.js create mode 100644 tests/index.js diff --git a/CHANGELOG.md b/CHANGELOG.md index a46b1b0326..7febb37111 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [Perf] `ExportMap`: Improve `ExportMap.for` performance on larger codebases ([#2756], thanks [@leipert]) - [`no-extraneous-dependencies`]/TypeScript: do not error when importing inline type from dev dependencies ([#1820], thanks [@andyogo]) - [`newline-after-import`]/TypeScript: do not error when re-exporting a namespaced import ([#2832], thanks [@laurens-dg]) -* [`order`]: partial fix for [#2687] (thanks [@ljharb]) +- [`order`]: partial fix for [#2687] (thanks [@ljharb]) - [`no-duplicates`]: Detect across type and regular imports ([#2835], thanks [@benkrejci]) +- [`extensions`]: handle `.` and `..` properly ([#2778], thanks [@benasher44]) ### Changed - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) @@ -1076,6 +1077,7 @@ for info on changes for earlier releases. [#2835]: https://github.com/import-js/eslint-plugin-import/pull/2835 [#2832]: https://github.com/import-js/eslint-plugin-import/pull/2832 +[#2778]: https://github.com/import-js/eslint-plugin-import/pull/2778 [#2756]: https://github.com/import-js/eslint-plugin-import/pull/2756 [#2754]: https://github.com/import-js/eslint-plugin-import/pull/2754 [#2748]: https://github.com/import-js/eslint-plugin-import/pull/2748 @@ -1649,6 +1651,7 @@ for info on changes for earlier releases. [@BarryThePenguin]: https://github.com/BarryThePenguin [@be5invis]: https://github.com/be5invis [@beatrizrezener]: https://github.com/beatrizrezener +[@benasher44]: https://github.com/benasher44 [@benkrejci]: https://github.com/benkrejci [@benmosher]: https://github.com/benmosher [@benmunro]: https://github.com/benmunro diff --git a/src/rules/extensions.js b/src/rules/extensions.js index 50debc6c8c..3acefcd31b 100644 --- a/src/rules/extensions.js +++ b/src/rules/extensions.js @@ -130,6 +130,7 @@ module.exports = { } function isExternalRootModule(file) { + if (file === '.' || file === '..') { return false; } const slashCount = file.split('/').length - 1; if (slashCount === 0) { return true; } diff --git a/tests/files/internal-modules/test.js b/tests/files/internal-modules/test.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/index.js b/tests/index.js new file mode 100644 index 0000000000..abc02b839c --- /dev/null +++ b/tests/index.js @@ -0,0 +1 @@ +export * from './files'; diff --git a/tests/src/rules/extensions.js b/tests/src/rules/extensions.js index ede1a8d88a..22a5fc3023 100644 --- a/tests/src/rules/extensions.js +++ b/tests/src/rules/extensions.js @@ -266,6 +266,26 @@ ruleTester.run('extensions', rule, { ], }), + test({ + code: [ + 'import barjs from "."', + 'import barjs2 from ".."', + ].join('\n'), + options: [ 'always' ], + errors: [ + { + message: 'Missing file extension "js" for "."', + line: 1, + column: 19, + }, + { + message: 'Missing file extension "js" for ".."', + line: 2, + column: 20, + }, + ], + }), + test({ code: [ 'import barjs from "./bar.js"', @@ -594,6 +614,35 @@ ruleTester.run('extensions', rule, { }, ], }), + + // TODO: properly ignore packages resolved via relative imports + test({ + code: [ + 'import * as test from "."', + ].join('\n'), + filename: testFilePath('./internal-modules/test.js'), + options: [ 'ignorePackages' ], + errors: [ + { + message: 'Missing file extension for "."', + line: 1, + }, + ], + }), + // TODO: properly ignore packages resolved via relative imports + test({ + code: [ + 'import * as test from ".."', + ].join('\n'), + filename: testFilePath('./internal-modules/plugins/plugin.js'), + options: [ 'ignorePackages' ], + errors: [ + { + message: 'Missing file extension for ".."', + line: 1, + }, + ], + }), ], }); From 68bf51019b0e9ee86c572393781a75b9dba2ea8b Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 25 Jul 2023 22:52:17 -0700 Subject: [PATCH 38/49] [eslint] enable `array-bracket-spacing` --- .eslintrc | 1 + src/rules/extensions.js | 2 +- src/rules/no-absolute-path.js | 2 +- tests/src/core/hash.js | 8 +- tests/src/core/ignore.js | 4 +- tests/src/core/parse.js | 4 +- tests/src/core/resolve.js | 10 +- tests/src/rules/extensions.js | 120 +++++------ tests/src/rules/named.js | 4 +- tests/src/rules/namespace.js | 18 +- tests/src/rules/newline-after-import.js | 60 +++--- tests/src/rules/no-amd.js | 8 +- tests/src/rules/no-commonjs.js | 28 +-- tests/src/rules/no-extraneous-dependencies.js | 4 +- tests/src/rules/no-internal-modules.js | 204 +++++++++--------- tests/src/rules/no-named-as-default.js | 16 +- tests/src/rules/no-namespace.js | 18 +- tests/src/rules/no-relative-packages.js | 16 +- tests/src/rules/no-relative-parent-imports.js | 18 +- tests/src/rules/no-unresolved.js | 6 +- tests/src/rules/no-useless-path-segments.js | 38 ++-- tests/src/rules/order.js | 2 +- utils/ignore.js | 2 +- 23 files changed, 297 insertions(+), 296 deletions(-) diff --git a/.eslintrc b/.eslintrc index 709a474484..932055e8d4 100644 --- a/.eslintrc +++ b/.eslintrc @@ -19,6 +19,7 @@ "ecmaVersion": 2020, }, "rules": { + "array-bracket-spacing": [2, "never"], "arrow-body-style": [2, "as-needed"], "arrow-parens": [2, "always"], "arrow-spacing": [2, { "before": true, "after": true }], diff --git a/src/rules/extensions.js b/src/rules/extensions.js index 3acefcd31b..b1e5c6d9f1 100644 --- a/src/rules/extensions.js +++ b/src/rules/extensions.js @@ -5,7 +5,7 @@ import { isBuiltIn, isExternalModule, isScoped } from '../core/importType'; import moduleVisitor from 'eslint-module-utils/moduleVisitor'; import docsUrl from '../docsUrl'; -const enumValues = { enum: [ 'always', 'ignorePackages', 'never' ] }; +const enumValues = { enum: ['always', 'ignorePackages', 'never'] }; const patternProperties = { type: 'object', patternProperties: { '.*': enumValues }, diff --git a/src/rules/no-absolute-path.js b/src/rules/no-absolute-path.js index a5498ec765..04f67383f2 100644 --- a/src/rules/no-absolute-path.js +++ b/src/rules/no-absolute-path.js @@ -12,7 +12,7 @@ module.exports = { url: docsUrl('no-absolute-path'), }, fixable: 'code', - schema: [ makeOptionsSchema() ], + schema: [makeOptionsSchema()], }, create(context) { diff --git a/tests/src/core/hash.js b/tests/src/core/hash.js index 1d6a9eb85c..785b8abc34 100644 --- a/tests/src/core/hash.js +++ b/tests/src/core/hash.js @@ -29,7 +29,7 @@ describe('hash', function () { }); it('handles Array instances', function () { - expectHash(hashify([ 'a string' ]), '["a string",]'); + expectHash(hashify(['a string']), '["a string",]'); }); it('handles empty Array instances', function () { @@ -45,13 +45,13 @@ describe('hash', function () { }); it('handles nested Object and Array instances', function () { - expectHash(hashify({ foo: 123.456, 'a key': 'a value', obj: { arr: [ { def: 'ghi' } ] } }), '{"a key":"a value","foo":123.456,"obj":{"arr":[{"def":"ghi",},],},}'); + expectHash(hashify({ foo: 123.456, 'a key': 'a value', obj: { arr: [{ def: 'ghi' }] } }), '{"a key":"a value","foo":123.456,"obj":{"arr":[{"def":"ghi",},],},}'); }); }); describe('hashArray', function () { it('handles Array instances', function () { - expectHash(hashArray([ 'a string' ]), '["a string",]'); + expectHash(hashArray(['a string']), '["a string",]'); }); it('handles empty Array instances', function () { @@ -69,7 +69,7 @@ describe('hash', function () { }); it('handles nested Object and Array instances', function () { - expectHash(hashObject({ foo: 123.456, 'a key': 'a value', obj: { arr: [ { def: 'ghi' } ] } }), '{"a key":"a value","foo":123.456,"obj":{"arr":[{"def":"ghi",},],},}'); + expectHash(hashObject({ foo: 123.456, 'a key': 'a value', obj: { arr: [{ def: 'ghi' }] } }), '{"a key":"a value","foo":123.456,"obj":{"arr":[{"def":"ghi",},],},}'); }); }); diff --git a/tests/src/core/ignore.js b/tests/src/core/ignore.js index 2b2126c8b5..3212781363 100644 --- a/tests/src/core/ignore.js +++ b/tests/src/core/ignore.js @@ -19,7 +19,7 @@ describe('ignore', function () { }); it('ignores paths with invalid extensions when configured with import/extensions', function () { - const testContext = utils.testContext({ 'import/extensions': [ '.js', '.jsx', '.ts' ] }); + const testContext = utils.testContext({ 'import/extensions': ['.js', '.jsx', '.ts'] }); expect(isIgnored('../files/foo.js', testContext)).to.equal(false); @@ -45,7 +45,7 @@ describe('ignore', function () { }); it('can be configured with import/extensions', function () { - const testContext = utils.testContext({ 'import/extensions': [ '.foo', '.bar' ] }); + const testContext = utils.testContext({ 'import/extensions': ['.foo', '.bar'] }); expect(hasValidExtension('../files/foo.foo', testContext)).to.equal(true); diff --git a/tests/src/core/parse.js b/tests/src/core/parse.js index 9c59453602..275b93982b 100644 --- a/tests/src/core/parse.js +++ b/tests/src/core/parse.js @@ -76,7 +76,7 @@ describe('parse(content, { settings, ecmaFeatures })', function () { const parseSpy = sinon.spy(); const parserOptions = { ecmaFeatures: { jsx: true } }; parseStubParser.parse = parseSpy; - expect(parse.bind(null, path, content, { settings: { 'import/parsers': { [parseStubParserPath]: [ '.js' ] } }, parserPath: null, parserOptions })).not.to.throw(Error); + expect(parse.bind(null, path, content, { settings: { 'import/parsers': { [parseStubParserPath]: ['.js'] } }, parserPath: null, parserOptions })).not.to.throw(Error); expect(parseSpy.callCount, 'custom parser to be called once').to.equal(1); }); @@ -123,7 +123,7 @@ describe('parse(content, { settings, ecmaFeatures })', function () { it('prefers parsers specified in the settings over languageOptions.parser', () => { const parseSpy = sinon.spy(); parseStubParser.parse = parseSpy; - expect(parse.bind(null, path, content, { settings: { 'import/parsers': { [parseStubParserPath]: [ '.js' ] } }, parserPath: null, languageOptions: { parser: { parse() {} } } })).not.to.throw(Error); + expect(parse.bind(null, path, content, { settings: { 'import/parsers': { [parseStubParserPath]: ['.js'] } }, parserPath: null, languageOptions: { parser: { parse() {} } } })).not.to.throw(Error); expect(parseSpy.callCount, 'custom parser to be called once').to.equal(1); }); diff --git a/tests/src/core/resolve.js b/tests/src/core/resolve.js index 6b69fb7f12..0db9b05f49 100644 --- a/tests/src/core/resolve.js +++ b/tests/src/core/resolve.js @@ -86,7 +86,7 @@ describe('resolve', function () { }); it('respects import/resolver as array of strings', function () { - const testContext = utils.testContext({ 'import/resolver': [ './foo-bar-resolver-v2', './foo-bar-resolver-v1' ] }); + const testContext = utils.testContext({ 'import/resolver': ['./foo-bar-resolver-v2', './foo-bar-resolver-v1'] }); expect(resolve( '../files/foo', @@ -104,7 +104,7 @@ describe('resolve', function () { }); it('respects import/resolver as array of objects', function () { - const testContext = utils.testContext({ 'import/resolver': [ { './foo-bar-resolver-v2': {} }, { './foo-bar-resolver-v1': {} } ] }); + const testContext = utils.testContext({ 'import/resolver': [{ './foo-bar-resolver-v2': {} }, { './foo-bar-resolver-v1': {} }] }); expect(resolve( '../files/foo', @@ -254,7 +254,7 @@ describe('resolve', function () { }); it('respects import/resolver as array of strings', function () { - const testContext = utils.testContext({ 'import/resolver': [ './foo-bar-resolver-v2', './foo-bar-resolver-v1' ] }); + const testContext = utils.testContext({ 'import/resolver': ['./foo-bar-resolver-v2', './foo-bar-resolver-v1'] }); expect(resolve( '../files/foo', @@ -272,7 +272,7 @@ describe('resolve', function () { }); it('respects import/resolver as array of objects', function () { - const testContext = utils.testContext({ 'import/resolver': [ { './foo-bar-resolver-v2': {} }, { './foo-bar-resolver-v1': {} } ] }); + const testContext = utils.testContext({ 'import/resolver': [{ './foo-bar-resolver-v2': {} }, { './foo-bar-resolver-v1': {} }] }); expect(resolve( '../files/foo', @@ -386,7 +386,7 @@ describe('resolve', function () { 'import/cache': { lifetime: 1 }, }); - const infiniteContexts = [ '∞', 'Infinity' ].map((inf) => [inf, + const infiniteContexts = ['∞', 'Infinity'].map((inf) => [inf, utils.testContext({ 'import/cache': { lifetime: inf }, })]); diff --git a/tests/src/rules/extensions.js b/tests/src/rules/extensions.js index 22a5fc3023..14d84eaa62 100644 --- a/tests/src/rules/extensions.js +++ b/tests/src/rules/extensions.js @@ -11,18 +11,18 @@ ruleTester.run('extensions', rule, { test({ code: 'import dot from "./file.with.dot"' }), test({ code: 'import a from "a/index.js"', - options: [ 'always' ], + options: ['always'], }), test({ code: 'import dot from "./file.with.dot.js"', - options: [ 'always' ], + options: ['always'], }), test({ code: [ 'import a from "a"', 'import packageConfig from "./package.json"', ].join('\n'), - options: [ { json: 'always', js: 'never' } ], + options: [{ json: 'always', js: 'never' }], }), test({ code: [ @@ -30,8 +30,8 @@ ruleTester.run('extensions', rule, { 'import component from "./bar.jsx"', 'import data from "./bar.json"', ].join('\n'), - options: [ 'never' ], - settings: { 'import/resolve': { extensions: [ '.js', '.jsx', '.json' ] } }, + options: ['never'], + settings: { 'import/resolve': { extensions: ['.js', '.jsx', '.json'] } }, }), test({ @@ -40,8 +40,8 @@ ruleTester.run('extensions', rule, { 'import barjson from "./bar.json"', 'import barhbs from "./bar.hbs"', ].join('\n'), - options: [ 'always', { js: 'never', jsx: 'never' } ], - settings: { 'import/resolve': { extensions: [ '.js', '.jsx', '.json', '.hbs' ] } }, + options: ['always', { js: 'never', jsx: 'never' }], + settings: { 'import/resolve': { extensions: ['.js', '.jsx', '.json', '.hbs'] } }, }), test({ @@ -49,16 +49,16 @@ ruleTester.run('extensions', rule, { 'import bar from "./bar.js"', 'import pack from "./package"', ].join('\n'), - options: [ 'never', { js: 'always', json: 'never' } ], - settings: { 'import/resolve': { extensions: [ '.js', '.json' ] } }, + options: ['never', { js: 'always', json: 'never' }], + settings: { 'import/resolve': { extensions: ['.js', '.json'] } }, }), // unresolved (#271/#295) test({ code: 'import path from "path"' }), - test({ code: 'import path from "path"', options: [ 'never' ] }), - test({ code: 'import path from "path"', options: [ 'always' ] }), - test({ code: 'import thing from "./fake-file.js"', options: [ 'always' ] }), - test({ code: 'import thing from "non-package"', options: [ 'never' ] }), + test({ code: 'import path from "path"', options: ['never'] }), + test({ code: 'import path from "path"', options: ['always'] }), + test({ code: 'import thing from "./fake-file.js"', options: ['always'] }), + test({ code: 'import thing from "non-package"', options: ['never'] }), test({ code: ` @@ -67,7 +67,7 @@ ruleTester.run('extensions', rule, { import Component from './Component.jsx' import express from 'express' `, - options: [ 'ignorePackages' ], + options: ['ignorePackages'], }), test({ @@ -77,7 +77,7 @@ ruleTester.run('extensions', rule, { import Component from './Component.jsx' import express from 'express' `, - options: [ 'always', { ignorePackages: true } ], + options: ['always', { ignorePackages: true }], }), test({ @@ -87,16 +87,16 @@ ruleTester.run('extensions', rule, { import Component from './Component' import express from 'express' `, - options: [ 'never', { ignorePackages: true } ], + options: ['never', { ignorePackages: true }], }), test({ code: 'import exceljs from "exceljs"', - options: [ 'always', { js: 'never', jsx: 'never' } ], + options: ['always', { js: 'never', jsx: 'never' }], filename: testFilePath('./internal-modules/plugins/plugin.js'), settings: { 'import/resolver': { - node: { extensions: [ '.js', '.jsx', '.json' ] }, + node: { extensions: ['.js', '.jsx', '.json'] }, webpack: { config: 'webpack.empty.config.js' }, }, }, @@ -108,14 +108,14 @@ ruleTester.run('extensions', rule, { 'export { foo } from "./foo.js"', 'let bar; export { bar }', ].join('\n'), - options: [ 'always' ], + options: ['always'], }), test({ code: [ 'export { foo } from "./foo"', 'let bar; export { bar }', ].join('\n'), - options: [ 'never' ], + options: ['never'], }), // Root packages should be ignored and they are names not files @@ -125,17 +125,17 @@ ruleTester.run('extensions', rule, { 'import lib2 from "pgk/package"', 'import lib3 from "@name/pkg.js"', ].join('\n'), - options: [ 'never' ], + options: ['never'], }), // Query strings. test({ code: 'import bare from "./foo?a=True.ext"', - options: [ 'never' ], + options: ['never'], }), test({ code: 'import bare from "./foo.js?a=True"', - options: [ 'always' ], + options: ['always'], }), test({ @@ -144,22 +144,22 @@ ruleTester.run('extensions', rule, { 'import lib2 from "pgk/package.js"', 'import lib3 from "@name/pkg"', ].join('\n'), - options: [ 'always' ], + options: ['always'], }), ], invalid: [ test({ code: 'import a from "a/index.js"', - errors: [ { + errors: [{ message: 'Unexpected use of file extension "js" for "a/index.js"', line: 1, column: 15, - } ], + }], }), test({ code: 'import dot from "./file.with.dot"', - options: [ 'always' ], + options: ['always'], errors: [ { message: 'Missing file extension "js" for "./file.with.dot"', @@ -173,8 +173,8 @@ ruleTester.run('extensions', rule, { 'import a from "a/index.js"', 'import packageConfig from "./package"', ].join('\n'), - options: [ { json: 'always', js: 'never' } ], - settings: { 'import/resolve': { extensions: [ '.js', '.json' ] } }, + options: [{ json: 'always', js: 'never' }], + settings: { 'import/resolve': { extensions: ['.js', '.json'] } }, errors: [ { message: 'Unexpected use of file extension "js" for "a/index.js"', @@ -194,8 +194,8 @@ ruleTester.run('extensions', rule, { 'import component from "./bar.jsx"', 'import data from "./bar.json"', ].join('\n'), - options: [ 'never' ], - settings: { 'import/resolve': { extensions: [ '.js', '.jsx', '.json' ] } }, + options: ['never'], + settings: { 'import/resolve': { extensions: ['.js', '.jsx', '.json'] } }, errors: [ { message: 'Unexpected use of file extension "js" for "./bar.js"', @@ -210,8 +210,8 @@ ruleTester.run('extensions', rule, { 'import component from "./bar.jsx"', 'import data from "./bar.json"', ].join('\n'), - options: [ { json: 'always', js: 'never', jsx: 'never' } ], - settings: { 'import/resolve': { extensions: [ '.js', '.jsx', '.json' ] } }, + options: [{ json: 'always', js: 'never', jsx: 'never' }], + settings: { 'import/resolve': { extensions: ['.js', '.jsx', '.json'] } }, errors: [ { message: 'Unexpected use of file extension "js" for "./bar.js"', @@ -226,8 +226,8 @@ ruleTester.run('extensions', rule, { 'import component from "./bar.jsx"', 'import data from "./bar.json"', ].join('\n'), - options: [ { json: 'always', js: 'never', jsx: 'never' } ], - settings: { 'import/resolve': { extensions: [ '.jsx', '.json', '.js' ] } }, + options: [{ json: 'always', js: 'never', jsx: 'never' }], + settings: { 'import/resolve': { extensions: ['.jsx', '.json', '.js'] } }, errors: [ { message: 'Unexpected use of file extension "jsx" for "./bar.jsx"', @@ -255,8 +255,8 @@ ruleTester.run('extensions', rule, { 'import barjson from "./bar.json"', 'import barnone from "./bar"', ].join('\n'), - options: [ 'always', { json: 'always', js: 'never', jsx: 'never' } ], - settings: { 'import/resolve': { extensions: [ '.js', '.jsx', '.json' ] } }, + options: ['always', { json: 'always', js: 'never', jsx: 'never' }], + settings: { 'import/resolve': { extensions: ['.js', '.jsx', '.json'] } }, errors: [ { message: 'Unexpected use of file extension "js" for "./bar.js"', @@ -271,7 +271,7 @@ ruleTester.run('extensions', rule, { 'import barjs from "."', 'import barjs2 from ".."', ].join('\n'), - options: [ 'always' ], + options: ['always'], errors: [ { message: 'Missing file extension "js" for "."', @@ -292,8 +292,8 @@ ruleTester.run('extensions', rule, { 'import barjson from "./bar.json"', 'import barnone from "./bar"', ].join('\n'), - options: [ 'never', { json: 'always', js: 'never', jsx: 'never' } ], - settings: { 'import/resolve': { extensions: [ '.js', '.jsx', '.json' ] } }, + options: ['never', { json: 'always', js: 'never', jsx: 'never' }], + settings: { 'import/resolve': { extensions: ['.js', '.jsx', '.json'] } }, errors: [ { message: 'Unexpected use of file extension "js" for "./bar.js"', @@ -306,7 +306,7 @@ ruleTester.run('extensions', rule, { // unresolved (#271/#295) test({ code: 'import thing from "./fake-file.js"', - options: [ 'never' ], + options: ['never'], errors: [ { message: 'Unexpected use of file extension "js" for "./fake-file.js"', @@ -317,7 +317,7 @@ ruleTester.run('extensions', rule, { }), test({ code: 'import thing from "non-package/test"', - options: [ 'always' ], + options: ['always'], errors: [ { message: 'Missing file extension for "non-package/test"', @@ -329,7 +329,7 @@ ruleTester.run('extensions', rule, { test({ code: 'import thing from "@name/pkg/test"', - options: [ 'always' ], + options: ['always'], errors: [ { message: 'Missing file extension for "@name/pkg/test"', @@ -341,7 +341,7 @@ ruleTester.run('extensions', rule, { test({ code: 'import thing from "@name/pkg/test.js"', - options: [ 'never' ], + options: ['never'], errors: [ { message: 'Unexpected use of file extension "js" for "@name/pkg/test.js"', @@ -361,7 +361,7 @@ ruleTester.run('extensions', rule, { import chart from '@/configs/chart' import express from 'express' `, - options: [ 'always', { ignorePackages: true } ], + options: ['always', { ignorePackages: true }], errors: [ { message: 'Missing file extension for "./Component"', @@ -386,7 +386,7 @@ ruleTester.run('extensions', rule, { import chart from '@/configs/chart' import express from 'express' `, - options: [ 'ignorePackages' ], + options: ['ignorePackages'], errors: [ { message: 'Missing file extension for "./Component"', @@ -419,7 +419,7 @@ ruleTester.run('extensions', rule, { column: 31, }, ], - options: [ 'never', { ignorePackages: true } ], + options: ['never', { ignorePackages: true }], }), test({ @@ -435,7 +435,7 @@ ruleTester.run('extensions', rule, { column: 31, }, ], - options: [ 'always', { pattern: { jsx: 'never' } } ], + options: ['always', { pattern: { jsx: 'never' } }], }), // export (#964) @@ -444,7 +444,7 @@ ruleTester.run('extensions', rule, { 'export { foo } from "./foo"', 'let bar; export { bar }', ].join('\n'), - options: [ 'always' ], + options: ['always'], errors: [ { message: 'Missing file extension for "./foo"', @@ -458,7 +458,7 @@ ruleTester.run('extensions', rule, { 'export { foo } from "./foo.js"', 'let bar; export { bar }', ].join('\n'), - options: [ 'never' ], + options: ['never'], errors: [ { message: 'Unexpected use of file extension "js" for "./foo.js"', @@ -471,7 +471,7 @@ ruleTester.run('extensions', rule, { // Query strings. test({ code: 'import withExtension from "./foo.js?a=True"', - options: [ 'never' ], + options: ['never'], errors: [ { message: 'Unexpected use of file extension "js" for "./foo.js?a=True"', @@ -482,7 +482,7 @@ ruleTester.run('extensions', rule, { }), test({ code: 'import withoutExtension from "./foo?a=True.ext"', - options: [ 'always' ], + options: ['always'], errors: [ { message: 'Missing file extension for "./foo?a=True.ext"', @@ -497,7 +497,7 @@ ruleTester.run('extensions', rule, { 'const { foo } = require("./foo")', 'export { foo }', ].join('\n'), - options: [ 'always' ], + options: ['always'], errors: [ { message: 'Missing file extension for "./foo"', @@ -511,7 +511,7 @@ ruleTester.run('extensions', rule, { 'const { foo } = require("./foo.js")', 'export { foo }', ].join('\n'), - options: [ 'never' ], + options: ['never'], errors: [ { message: 'Unexpected use of file extension "js" for "./foo.js"', @@ -524,7 +524,7 @@ ruleTester.run('extensions', rule, { // export { } from test({ code: 'export { foo } from "./foo"', - options: [ 'always' ], + options: ['always'], errors: [ { message: 'Missing file extension for "./foo"', @@ -552,7 +552,7 @@ ruleTester.run('extensions', rule, { }), test({ code: 'export { foo } from "./foo.js"', - options: [ 'never' ], + options: ['never'], errors: [ { message: 'Unexpected use of file extension "js" for "./foo.js"', @@ -565,7 +565,7 @@ ruleTester.run('extensions', rule, { // export * from test({ code: 'export * from "./foo"', - options: [ 'always' ], + options: ['always'], errors: [ { message: 'Missing file extension for "./foo"', @@ -576,7 +576,7 @@ ruleTester.run('extensions', rule, { }), test({ code: 'export * from "./foo.js"', - options: [ 'never' ], + options: ['never'], errors: [ { message: 'Unexpected use of file extension "js" for "./foo.js"', @@ -621,7 +621,7 @@ ruleTester.run('extensions', rule, { 'import * as test from "."', ].join('\n'), filename: testFilePath('./internal-modules/test.js'), - options: [ 'ignorePackages' ], + options: ['ignorePackages'], errors: [ { message: 'Missing file extension for "."', @@ -635,7 +635,7 @@ ruleTester.run('extensions', rule, { 'import * as test from ".."', ].join('\n'), filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ 'ignorePackages' ], + options: ['ignorePackages'], errors: [ { message: 'Missing file extension for ".."', diff --git a/tests/src/rules/named.js b/tests/src/rules/named.js index 110cfff52a..227bffc80d 100644 --- a/tests/src/rules/named.js +++ b/tests/src/rules/named.js @@ -208,7 +208,7 @@ ruleTester.run('named', rule, { invalid: [].concat( test({ code: 'import { somethingElse } from "./test-module"', - errors: [ error('somethingElse', './test-module') ] }), + errors: [error('somethingElse', './test-module')] }), test({ code: 'import { baz } from "./bar"', errors: [error('baz', './bar')] }), @@ -330,7 +330,7 @@ ruleTester.run('named', rule, { // es2022: Arbitrary module namespace identifier names testVersion('>= 8.7', () => ({ code: 'import { "somethingElse" as somethingElse } from "./test-module"', - errors: [ error('somethingElse', './test-module', 'Literal') ], + errors: [error('somethingElse', './test-module', 'Literal')], parserOptions: { ecmaVersion: 2022 }, })), testVersion('>= 8.7', () => ({ diff --git a/tests/src/rules/namespace.js b/tests/src/rules/namespace.js index d368fd3fe9..1475ae9b7d 100644 --- a/tests/src/rules/namespace.js +++ b/tests/src/rules/namespace.js @@ -284,7 +284,7 @@ const invalid = [].concat( test({ code: "import b from './deep/default'; console.log(b.e)", - errors: [ "'e' not found in imported namespace 'b'." ], + errors: ["'e' not found in imported namespace 'b'."], }), // respect hoisting @@ -317,12 +317,12 @@ const invalid = [].concat( // 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'." ], + 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'." ], + errors: ["'e' not found in deeply imported namespace 'b.c'."], parserOptions: { ecmaVersion: 2022 }, })), ); @@ -345,32 +345,32 @@ const invalid = [].concat( test({ parser, code: `import * as a from "./${folder}/a"; console.log(a.b.e)`, - errors: [ "'e' not found in deeply imported namespace 'a.b'." ], + errors: ["'e' not found in deeply imported namespace 'a.b'."], }), test({ parser, code: `import { b } from "./${folder}/a"; console.log(b.e)`, - errors: [ "'e' not found in imported namespace 'b'." ], + errors: ["'e' not found in imported namespace 'b'."], }), test({ parser, code: `import * as a from "./${folder}/a"; console.log(a.b.c.e)`, - errors: [ "'e' not found in deeply imported namespace 'a.b.c'." ], + errors: ["'e' not found in deeply imported namespace 'a.b.c'."], }), test({ parser, code: `import { b } from "./${folder}/a"; console.log(b.c.e)`, - errors: [ "'e' not found in deeply imported namespace 'b.c'." ], + errors: ["'e' not found in deeply imported namespace 'b.c'."], }), test({ parser, code: `import * as a from "./${folder}/a"; var {b:{ e }} = a`, - errors: [ "'e' not found in deeply imported namespace 'a.b'." ], + errors: ["'e' not found in deeply imported namespace 'a.b'."], }), test({ parser, code: `import * as a from "./${folder}/a"; var {b:{c:{ e }}} = a`, - errors: [ "'e' not found in deeply imported namespace 'a.b.c'." ], + errors: ["'e' not found in deeply imported namespace 'a.b.c'."], })); }); diff --git a/tests/src/rules/newline-after-import.js b/tests/src/rules/newline-after-import.js index 9ec6eb757b..8f3338eee8 100644 --- a/tests/src/rules/newline-after-import.js +++ b/tests/src/rules/newline-after-import.js @@ -346,11 +346,11 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { // some comment var foo = 'bar'; `, - errors: [ { + errors: [{ line: 3, column: 1, message: IMPORT_ERROR_MESSAGE, - } ], + }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, options: [{ considerComments: true }], }, @@ -373,11 +373,11 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { **/ var bar = 42; `, - errors: [ { + errors: [{ line: 3, column: 9, message: IMPORT_ERROR_MESSAGE, - } ], + }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, options: [{ considerComments: true }], }, @@ -394,54 +394,54 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { // Some random single line comment var bar = 42; `, - errors: [ { + errors: [{ line: 3, column: 9, message: IMPORT_ERROR_MESSAGE, - } ], + }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, options: [{ considerComments: true, count: 1 }], }, { code: `import foo from 'foo';\nexport default function() {};`, output: `import foo from 'foo';\n\nexport default function() {};`, - errors: [ { + errors: [{ line: 1, column: 1, message: IMPORT_ERROR_MESSAGE, - } ], + }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { code: `import foo from 'foo';\n\nexport default function() {};`, output: `import foo from 'foo';\n\n\nexport default function() {};`, options: [{ count: 2 }], - errors: [ { + errors: [{ line: 1, column: 1, message: IMPORT_ERROR_MESSAGE_MULTIPLE(2), - } ], + }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { code: `var foo = require('foo-module');\nvar something = 123;`, output: `var foo = require('foo-module');\n\nvar something = 123;`, - errors: [ { + errors: [{ line: 1, column: 1, message: REQUIRE_ERROR_MESSAGE, - } ], + }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { code: `import foo from 'foo';\nexport default function() {};`, output: `import foo from 'foo';\n\nexport default function() {};`, options: [{ count: 1 }], - errors: [ { + errors: [{ line: 1, column: 1, message: IMPORT_ERROR_MESSAGE, - } ], + }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { @@ -495,20 +495,20 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { { code: `var path = require('path');\nvar foo = require('foo');\nvar bar = 42;`, output: `var path = require('path');\nvar foo = require('foo');\n\nvar bar = 42;`, - errors: [ { + errors: [{ line: 2, column: 1, message: REQUIRE_ERROR_MESSAGE, - } ], + }], }, { code: `var assign = Object.assign || require('object-assign');\nvar foo = require('foo');\nvar bar = 42;`, output: `var assign = Object.assign || require('object-assign');\nvar foo = require('foo');\n\nvar bar = 42;`, - errors: [ { + errors: [{ line: 2, column: 1, message: REQUIRE_ERROR_MESSAGE, - } ], + }], }, { code: `require('a');\nfoo(require('b'), require('c'), require('d'));\nrequire('d');\nvar foo = 'bar';`, @@ -535,64 +535,64 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { { code: `import path from 'path';\nimport foo from 'foo';\nvar bar = 42;`, output: `import path from 'path';\nimport foo from 'foo';\n\nvar bar = 42;`, - errors: [ { + errors: [{ line: 2, column: 1, message: IMPORT_ERROR_MESSAGE, - } ], + }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { code: `import path from 'path';import foo from 'foo';var bar = 42;`, output: `import path from 'path';import foo from 'foo';\n\nvar bar = 42;`, - errors: [ { + errors: [{ line: 1, column: 25, message: IMPORT_ERROR_MESSAGE, - } ], + }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { code: `import foo from 'foo';\n@SomeDecorator(foo)\nclass Foo {}`, output: `import foo from 'foo';\n\n@SomeDecorator(foo)\nclass Foo {}`, - errors: [ { + errors: [{ line: 1, column: 1, message: IMPORT_ERROR_MESSAGE, - } ], + }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, parser: parsers.BABEL_OLD, }, { code: `var foo = require('foo');\n@SomeDecorator(foo)\nclass Foo {}`, output: `var foo = require('foo');\n\n@SomeDecorator(foo)\nclass Foo {}`, - errors: [ { + errors: [{ line: 1, column: 1, message: REQUIRE_ERROR_MESSAGE, - } ], + }], parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, parser: parsers.BABEL_OLD, }, { code: `// issue 10042\nimport foo from 'foo';\n@SomeDecorator(foo)\nexport default class Test {}`, output: `// issue 10042\nimport foo from 'foo';\n\n@SomeDecorator(foo)\nexport default class Test {}`, - errors: [ { + errors: [{ line: 2, column: 1, message: IMPORT_ERROR_MESSAGE, - } ], + }], parserOptions: { sourceType: 'module' }, parser: parsers.BABEL_OLD, }, { code: `// issue 1004\nconst foo = require('foo');\n@SomeDecorator(foo)\nexport default class Test {}`, output: `// issue 1004\nconst foo = require('foo');\n\n@SomeDecorator(foo)\nexport default class Test {}`, - errors: [ { + errors: [{ line: 2, column: 1, message: REQUIRE_ERROR_MESSAGE, - } ], + }], parserOptions: { sourceType: 'module' }, parser: parsers.BABEL_OLD, }, diff --git a/tests/src/rules/no-amd.js b/tests/src/rules/no-amd.js index 91e29234c8..5317aa8fde 100644 --- a/tests/src/rules/no-amd.js +++ b/tests/src/rules/no-amd.js @@ -28,10 +28,10 @@ ruleTester.run('no-amd', require('rules/no-amd'), { ], invalid: semver.satisfies(eslintPkg.version, '< 4.0.0') ? [] : [ - { code: 'define([], function() {})', errors: [ { message: 'Expected imports instead of AMD define().' }] }, - { code: 'define(["a"], function(a) { console.log(a); })', errors: [ { message: 'Expected imports instead of AMD define().' }] }, + { code: 'define([], function() {})', errors: [{ message: 'Expected imports instead of AMD define().' }] }, + { code: 'define(["a"], function(a) { console.log(a); })', errors: [{ message: 'Expected imports instead of AMD define().' }] }, - { code: 'require([], function() {})', errors: [ { message: 'Expected imports instead of AMD require().' }] }, - { code: 'require(["a"], function(a) { console.log(a); })', errors: [ { message: 'Expected imports instead of AMD require().' }] }, + { code: 'require([], function() {})', errors: [{ message: 'Expected imports instead of AMD require().' }] }, + { code: 'require(["a"], function(a) { console.log(a); })', errors: [{ message: 'Expected imports instead of AMD require().' }] }, ], }); diff --git a/tests/src/rules/no-commonjs.js b/tests/src/rules/no-commonjs.js index baa3b907f6..b7c0aa803f 100644 --- a/tests/src/rules/no-commonjs.js +++ b/tests/src/rules/no-commonjs.js @@ -69,47 +69,47 @@ ruleTester.run('no-commonjs', require('rules/no-commonjs'), { // imports ...semver.satisfies(eslintPkg.version, '< 4.0.0') ? [] : [ - { code: 'var x = require("x")', output: 'var x = require("x")', errors: [ { message: IMPORT_MESSAGE }] }, - { code: 'x = require("x")', output: 'x = require("x")', errors: [ { message: IMPORT_MESSAGE }] }, - { code: 'require("x")', output: 'require("x")', errors: [ { message: IMPORT_MESSAGE }] }, + { code: 'var x = require("x")', output: 'var x = require("x")', errors: [{ message: IMPORT_MESSAGE }] }, + { code: 'x = require("x")', output: 'x = require("x")', errors: [{ message: IMPORT_MESSAGE }] }, + { code: 'require("x")', output: 'require("x")', errors: [{ message: IMPORT_MESSAGE }] }, { code: 'require(`x`)', parserOptions: { ecmaVersion: 2015 }, output: 'require(`x`)', - errors: [ { message: IMPORT_MESSAGE }], + errors: [{ message: IMPORT_MESSAGE }], }, { code: 'if (typeof window !== "undefined") require("x")', options: [{ allowConditionalRequire: false }], output: 'if (typeof window !== "undefined") require("x")', - errors: [ { message: IMPORT_MESSAGE }], + errors: [{ message: IMPORT_MESSAGE }], }, { code: 'if (typeof window !== "undefined") { require("x") }', options: [{ allowConditionalRequire: false }], output: 'if (typeof window !== "undefined") { require("x") }', - errors: [ { message: IMPORT_MESSAGE }], + errors: [{ message: IMPORT_MESSAGE }], }, { code: 'try { require("x") } catch (error) {}', options: [{ allowConditionalRequire: false }], output: 'try { require("x") } catch (error) {}', - errors: [ { message: IMPORT_MESSAGE }], + errors: [{ message: IMPORT_MESSAGE }], }, ], // exports - { code: 'exports.face = "palm"', output: 'exports.face = "palm"', errors: [ { message: EXPORT_MESSAGE }] }, - { code: 'module.exports.face = "palm"', output: 'module.exports.face = "palm"', errors: [ { message: EXPORT_MESSAGE }] }, - { code: 'module.exports = face', output: 'module.exports = face', errors: [ { message: EXPORT_MESSAGE }] }, - { code: 'exports = module.exports = {}', output: 'exports = module.exports = {}', errors: [ { message: EXPORT_MESSAGE }] }, - { code: 'var x = module.exports = {}', output: 'var x = module.exports = {}', errors: [ { message: EXPORT_MESSAGE }] }, + { code: 'exports.face = "palm"', output: 'exports.face = "palm"', errors: [{ message: EXPORT_MESSAGE }] }, + { code: 'module.exports.face = "palm"', output: 'module.exports.face = "palm"', errors: [{ message: EXPORT_MESSAGE }] }, + { code: 'module.exports = face', output: 'module.exports = face', errors: [{ message: EXPORT_MESSAGE }] }, + { code: 'exports = module.exports = {}', output: 'exports = module.exports = {}', errors: [{ message: EXPORT_MESSAGE }] }, + { code: 'var x = module.exports = {}', output: 'var x = module.exports = {}', errors: [{ message: EXPORT_MESSAGE }] }, { code: 'module.exports = {}', options: ['allow-primitive-modules'], output: 'module.exports = {}', - errors: [ { message: EXPORT_MESSAGE }], + errors: [{ message: EXPORT_MESSAGE }], }, { code: 'var x = module.exports', options: ['allow-primitive-modules'], output: 'var x = module.exports', - errors: [ { message: EXPORT_MESSAGE }], + errors: [{ message: EXPORT_MESSAGE }], }, ], }); diff --git a/tests/src/rules/no-extraneous-dependencies.js b/tests/src/rules/no-extraneous-dependencies.js index 6f4e710f9d..21561615c6 100644 --- a/tests/src/rules/no-extraneous-dependencies.js +++ b/tests/src/rules/no-extraneous-dependencies.js @@ -57,7 +57,7 @@ ruleTester.run('no-extraneous-dependencies', rule, { // 'project' type test({ code: 'import "importType"', - settings: { 'import/resolver': { node: { paths: [ path.join(__dirname, '../../files') ] } } }, + settings: { 'import/resolver': { node: { paths: [path.join(__dirname, '../../files')] } } }, }), test({ code: 'import chai from "chai"', @@ -396,7 +396,7 @@ ruleTester.run('no-extraneous-dependencies', rule, { test({ code: 'import "not-a-dependency"', settings: { - 'import/resolver': { node: { paths: [ path.join(__dirname, '../../files') ] } }, + 'import/resolver': { node: { paths: [path.join(__dirname, '../../files')] } }, 'import/internal-regex': '^not-a-dependency.*', }, options: [{ includeInternal: true }], diff --git a/tests/src/rules/no-internal-modules.js b/tests/src/rules/no-internal-modules.js index 4a733d142a..c1c3015453 100644 --- a/tests/src/rules/no-internal-modules.js +++ b/tests/src/rules/no-internal-modules.js @@ -41,51 +41,51 @@ ruleTester.run('no-internal-modules', rule, { test({ code: 'import b from "../../api/service"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - allow: [ '**/api/*' ], - } ], + options: [{ + allow: ['**/api/*'], + }], }), test({ code: 'import "jquery/dist/jquery"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - allow: [ 'jquery/dist/*' ], - } ], + options: [{ + allow: ['jquery/dist/*'], + }], }), test({ code: 'import "./app/index.js";\nimport "./app/index"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - allow: [ '**/index{.js,}' ], - } ], + options: [{ + allow: ['**/index{.js,}'], + }], }), test({ code: 'import a from "./plugin2/thing"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ { - forbid: [ '**/api/*' ], - } ], + options: [{ + forbid: ['**/api/*'], + }], }), test({ code: 'const a = require("./plugin2/thing")', filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ { - forbid: [ '**/api/*' ], - } ], + options: [{ + forbid: ['**/api/*'], + }], }), test({ code: 'import b from "app/a"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - forbid: [ 'app/**/**' ], - } ], + options: [{ + forbid: ['app/**/**'], + }], }), test({ code: 'import b from "@org/package"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - forbid: [ '@org/package/*' ], - } ], + options: [{ + forbid: ['@org/package/*'], + }], }), // exports test({ @@ -103,23 +103,23 @@ ruleTester.run('no-internal-modules', rule, { test({ code: 'export {b} from "../../api/service"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - allow: [ '**/api/*' ], - } ], + options: [{ + allow: ['**/api/*'], + }], }), test({ code: 'export * from "jquery/dist/jquery"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - allow: [ 'jquery/dist/*' ], - } ], + options: [{ + allow: ['jquery/dist/*'], + }], }), test({ code: 'export * from "./app/index.js";\nexport * from "./app/index"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - allow: [ '**/index{.js,}' ], - } ], + options: [{ + allow: ['**/index{.js,}'], + }], }), test({ code: ` @@ -145,30 +145,30 @@ ruleTester.run('no-internal-modules', rule, { test({ code: 'export * from "./plugin2/thing"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ { - forbid: [ '**/api/*' ], - } ], + options: [{ + forbid: ['**/api/*'], + }], }), test({ code: 'export * from "app/a"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - forbid: [ 'app/**/**' ], - } ], + options: [{ + forbid: ['app/**/**'], + }], }), test({ code: 'export { b } from "@org/package"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - forbid: [ '@org/package/*' ], - } ], + options: [{ + forbid: ['@org/package/*'], + }], }), test({ code: 'export * from "./app/index.js";\nexport * from "./app/index"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - forbid: [ '**/index.ts' ], - } ], + options: [{ + forbid: ['**/index.ts'], + }], }), ], @@ -177,39 +177,39 @@ ruleTester.run('no-internal-modules', rule, { test({ code: 'import "./plugin2/index.js";\nimport "./plugin2/app/index"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ { - allow: [ '*/index.js' ], - } ], - errors: [ { + options: [{ + allow: ['*/index.js'], + }], + errors: [{ message: 'Reaching to "./plugin2/app/index" is not allowed.', line: 2, column: 8, - } ], + }], }), test({ code: 'import "./app/index.js"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - errors: [ { + errors: [{ message: 'Reaching to "./app/index.js" is not allowed.', line: 1, column: 8, - } ], + }], }), test({ code: 'import b from "./plugin2/internal"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - errors: [ { + errors: [{ message: 'Reaching to "./plugin2/internal" is not allowed.', line: 1, column: 15, - } ], + }], }), test({ code: 'import a from "../api/service/index"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ { - allow: [ '**/internal-modules/*' ], - } ], + options: [{ + allow: ['**/internal-modules/*'], + }], errors: [ { message: 'Reaching to "../api/service/index" is not allowed.', @@ -243,58 +243,58 @@ ruleTester.run('no-internal-modules', rule, { test({ code: 'import "./app/index.js"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - forbid: [ '*/app/*' ], - } ], - errors: [ { + options: [{ + forbid: ['*/app/*'], + }], + errors: [{ message: 'Reaching to "./app/index.js" is not allowed.', line: 1, column: 8, - } ], + }], }), test({ code: 'import b from "@org/package"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - forbid: [ '@org/**' ], - } ], - errors: [ { + options: [{ + forbid: ['@org/**'], + }], + errors: [{ message: 'Reaching to "@org/package" is not allowed.', line: 1, column: 15, - } ], + }], }), test({ code: 'import b from "app/a/b"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - forbid: [ 'app/**/**' ], - } ], - errors: [ { + options: [{ + forbid: ['app/**/**'], + }], + errors: [{ message: 'Reaching to "app/a/b" is not allowed.', line: 1, column: 15, - } ], + }], }), test({ code: 'import get from "lodash.get"', filename: testFilePath('./internal-modules/plugins/plugin2/index.js'), - options: [ { - forbid: [ 'lodash.*' ], - } ], - errors: [ { + options: [{ + forbid: ['lodash.*'], + }], + errors: [{ message: 'Reaching to "lodash.get" is not allowed.', line: 1, column: 17, - } ], + }], }), test({ code: 'import "./app/index.js";\nimport "./app/index"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - forbid: [ '**/index{.js,}' ], - } ], - errors: [ { + options: [{ + forbid: ['**/index{.js,}'], + }], + errors: [{ message: 'Reaching to "./app/index.js" is not allowed.', line: 1, column: 8, @@ -302,18 +302,18 @@ ruleTester.run('no-internal-modules', rule, { message: 'Reaching to "./app/index" is not allowed.', line: 2, column: 8, - } ], + }], }), test({ code: 'import "@/api/service";', - options: [ { - forbid: [ '**/api/*' ], - } ], - errors: [ { + options: [{ + forbid: ['**/api/*'], + }], + errors: [{ message: 'Reaching to "@/api/service" is not allowed.', line: 1, column: 8, - } ], + }], settings: { 'import/resolver': { webpack: { @@ -332,39 +332,39 @@ ruleTester.run('no-internal-modules', rule, { test({ code: 'export * from "./plugin2/index.js";\nexport * from "./plugin2/app/index"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ { - allow: [ '*/index.js' ], - } ], - errors: [ { + options: [{ + allow: ['*/index.js'], + }], + errors: [{ message: 'Reaching to "./plugin2/app/index" is not allowed.', line: 2, column: 15, - } ], + }], }), test({ code: 'export * from "./app/index.js"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - errors: [ { + errors: [{ message: 'Reaching to "./app/index.js" is not allowed.', line: 1, column: 15, - } ], + }], }), test({ code: 'export {b} from "./plugin2/internal"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - errors: [ { + errors: [{ message: 'Reaching to "./plugin2/internal" is not allowed.', line: 1, column: 17, - } ], + }], }), test({ code: 'export {a} from "../api/service/index"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ { - allow: [ '**/internal-modules/*' ], - } ], + options: [{ + allow: ['**/internal-modules/*'], + }], errors: [ { message: 'Reaching to "../api/service/index" is not allowed.', @@ -398,9 +398,9 @@ ruleTester.run('no-internal-modules', rule, { test({ code: 'export * from "./plugin2/thing"', filename: testFilePath('./internal-modules/plugins/plugin.js'), - options: [ { - forbid: [ '**/plugin2/*' ], - } ], + options: [{ + forbid: ['**/plugin2/*'], + }], errors: [ { message: 'Reaching to "./plugin2/thing" is not allowed.', @@ -412,9 +412,9 @@ ruleTester.run('no-internal-modules', rule, { test({ code: 'export * from "app/a"', filename: testFilePath('./internal-modules/plugins/plugin2/internal.js'), - options: [ { - forbid: [ '**' ], - } ], + options: [{ + forbid: ['**'], + }], errors: [ { message: 'Reaching to "app/a" is not allowed.', diff --git a/tests/src/rules/no-named-as-default.js b/tests/src/rules/no-named-as-default.js index 04ec28e615..c6646a4f0d 100644 --- a/tests/src/rules/no-named-as-default.js +++ b/tests/src/rules/no-named-as-default.js @@ -33,28 +33,28 @@ ruleTester.run('no-named-as-default', rule, { invalid: [].concat( test({ code: 'import foo from "./bar";', - errors: [ { + errors: [{ message: 'Using exported name \'foo\' as identifier for default export.', - type: 'ImportDefaultSpecifier' } ] }), + type: 'ImportDefaultSpecifier' }] }), test({ code: 'import foo, { foo as bar } from "./bar";', - errors: [ { + errors: [{ message: 'Using exported name \'foo\' as identifier for default export.', - type: 'ImportDefaultSpecifier' } ] }), + type: 'ImportDefaultSpecifier' }] }), // es7 test({ code: 'export foo from "./bar";', parser: parsers.BABEL_OLD, - errors: [ { + errors: [{ message: 'Using exported name \'foo\' as identifier for default export.', - type: 'ExportDefaultSpecifier' } ] }), + type: 'ExportDefaultSpecifier' }] }), test({ code: 'export foo, { foo as bar } from "./bar";', parser: parsers.BABEL_OLD, - errors: [ { + errors: [{ message: 'Using exported name \'foo\' as identifier for default export.', - type: 'ExportDefaultSpecifier' } ] }), + type: 'ExportDefaultSpecifier' }] }), test({ code: 'import foo from "./malformed.js"', diff --git a/tests/src/rules/no-namespace.js b/tests/src/rules/no-namespace.js index d75928c1d8..03a23e3dd7 100644 --- a/tests/src/rules/no-namespace.js +++ b/tests/src/rules/no-namespace.js @@ -20,7 +20,7 @@ const FIX_TESTS = semver.satisfies(eslintPkg.version, '>5.0.0') ? [ florp(bar); florp(baz); `.trim(), - errors: [ { + errors: [{ line: 1, column: 8, message: ERROR_MESSAGE, @@ -43,7 +43,7 @@ const FIX_TESTS = semver.satisfies(eslintPkg.version, '>5.0.0') ? [ florp(foo_bar); florp(foo_baz_1); `.trim(), - errors: [ { + errors: [{ line: 1, column: 8, message: ERROR_MESSAGE, @@ -64,7 +64,7 @@ const FIX_TESTS = semver.satisfies(eslintPkg.version, '>5.0.0') ? [ florp(foo_arg); } `.trim(), - errors: [ { + errors: [{ line: 1, column: 8, message: ERROR_MESSAGE, @@ -85,29 +85,29 @@ ruleTester.run('no-namespace', require('rules/no-namespace'), { test({ code: 'import * as foo from \'foo\';', output: 'import * as foo from \'foo\';', - errors: [ { + errors: [{ line: 1, column: 8, message: ERROR_MESSAGE, - } ], + }], }), test({ code: 'import defaultExport, * as foo from \'foo\';', output: 'import defaultExport, * as foo from \'foo\';', - errors: [ { + errors: [{ line: 1, column: 23, message: ERROR_MESSAGE, - } ], + }], }), test({ code: 'import * as foo from \'./foo\';', output: 'import * as foo from \'./foo\';', - errors: [ { + errors: [{ line: 1, column: 8, message: ERROR_MESSAGE, - } ], + }], }), ...FIX_TESTS, ], diff --git a/tests/src/rules/no-relative-packages.js b/tests/src/rules/no-relative-packages.js index 2d27bcc91e..6104aeb9ca 100644 --- a/tests/src/rules/no-relative-packages.js +++ b/tests/src/rules/no-relative-packages.js @@ -42,41 +42,41 @@ ruleTester.run('no-relative-packages', rule, { test({ code: 'import foo from "./package-named"', filename: testFilePath('./bar.js'), - errors: [ { + errors: [{ message: 'Relative import from another package is not allowed. Use `package-named` instead of `./package-named`', line: 1, column: 17, - } ], + }], output: 'import foo from "package-named"', }), test({ code: 'import foo from "../package-named"', filename: testFilePath('./package/index.js'), - errors: [ { + errors: [{ message: 'Relative import from another package is not allowed. Use `package-named` instead of `../package-named`', line: 1, column: 17, - } ], + }], output: 'import foo from "package-named"', }), test({ code: 'import foo from "../package-scoped"', filename: testFilePath('./package/index.js'), - errors: [ { + errors: [{ message: `Relative import from another package is not allowed. Use \`${normalize('@scope/package-named')}\` instead of \`../package-scoped\``, line: 1, column: 17, - } ], + }], output: `import foo from "@scope/package-named"`, }), test({ code: 'import bar from "../bar"', filename: testFilePath('./package-named/index.js'), - errors: [ { + errors: [{ message: `Relative import from another package is not allowed. Use \`${normalize('eslint-plugin-import/tests/files/bar')}\` instead of \`../bar\``, line: 1, column: 17, - } ], + }], output: `import bar from "eslint-plugin-import/tests/files/bar"`, }), ], diff --git a/tests/src/rules/no-relative-parent-imports.js b/tests/src/rules/no-relative-parent-imports.js index bfd4e16bcd..1af9b8cf8d 100644 --- a/tests/src/rules/no-relative-parent-imports.js +++ b/tests/src/rules/no-relative-parent-imports.js @@ -55,32 +55,32 @@ ruleTester.run('no-relative-parent-imports', rule, { invalid: [ test({ code: 'import foo from "../plugin.js"', - errors: [ { + errors: [{ message: 'Relative imports from parent directories are not allowed. Please either pass what you\'re importing through at runtime (dependency injection), move `index.js` to same directory as `../plugin.js` or consider making `../plugin.js` a package.', line: 1, column: 17, - } ], + }], }), test({ code: 'require("../plugin.js")', options: [{ commonjs: true }], - errors: [ { + errors: [{ message: 'Relative imports from parent directories are not allowed. Please either pass what you\'re importing through at runtime (dependency injection), move `index.js` to same directory as `../plugin.js` or consider making `../plugin.js` a package.', line: 1, column: 9, - } ], + }], }), test({ code: 'import("../plugin.js")', - errors: [ { + errors: [{ message: 'Relative imports from parent directories are not allowed. Please either pass what you\'re importing through at runtime (dependency injection), move `index.js` to same directory as `../plugin.js` or consider making `../plugin.js` a package.', line: 1, column: 8, - } ], + }], }), test({ code: 'import foo from "./../plugin.js"', - errors: [ { + errors: [{ message: 'Relative imports from parent directories are not allowed. Please either pass what you\'re importing through at runtime (dependency injection), move `index.js` to same directory as `./../plugin.js` or consider making `./../plugin.js` a package.', line: 1, column: 17, @@ -88,7 +88,7 @@ ruleTester.run('no-relative-parent-imports', rule, { }), test({ code: 'import foo from "../../api/service"', - errors: [ { + errors: [{ message: 'Relative imports from parent directories are not allowed. Please either pass what you\'re importing through at runtime (dependency injection), move `index.js` to same directory as `../../api/service` or consider making `../../api/service` a package.', line: 1, column: 17, @@ -96,7 +96,7 @@ ruleTester.run('no-relative-parent-imports', rule, { }), test({ code: 'import("../../api/service")', - errors: [ { + errors: [{ message: 'Relative imports from parent directories are not allowed. Please either pass what you\'re importing through at runtime (dependency injection), move `index.js` to same directory as `../../api/service` or consider making `../../api/service` a package.', line: 1, column: 8, diff --git a/tests/src/rules/no-unresolved.js b/tests/src/rules/no-unresolved.js index 109099c389..04a53d887b 100644 --- a/tests/src/rules/no-unresolved.js +++ b/tests/src/rules/no-unresolved.js @@ -387,7 +387,7 @@ ruleTester.run('no-unresolved (import/resolve legacy)', rule, { invalid: [ test({ code: 'import * as foo from "jsx-module/foo"', - errors: [ "Unable to resolve path to module 'jsx-module/foo'." ], + errors: ["Unable to resolve path to module 'jsx-module/foo'."], }), ], }); @@ -536,12 +536,12 @@ context('TypeScript', () => { invalid: [ test({ code: 'import { JSONSchema7Type } from "@types/json-schema";', - errors: [ "Unable to resolve path to module '@types/json-schema'." ], + errors: ["Unable to resolve path to module '@types/json-schema'."], parser, }), test({ code: 'export { JSONSchema7Type } from "@types/json-schema";', - errors: [ "Unable to resolve path to module '@types/json-schema'." ], + errors: ["Unable to resolve path to module '@types/json-schema'."], parser, }), ], diff --git a/tests/src/rules/no-useless-path-segments.js b/tests/src/rules/no-useless-path-segments.js index f960953503..d6d0395dea 100644 --- a/tests/src/rules/no-useless-path-segments.js +++ b/tests/src/rules/no-useless-path-segments.js @@ -42,49 +42,49 @@ function runResolverTests(resolver) { code: 'require("./../files/malformed.js")', output: 'require("../files/malformed.js")', options: [{ commonjs: true }], - errors: [ 'Useless path segments for "./../files/malformed.js", should be "../files/malformed.js"'], + errors: ['Useless path segments for "./../files/malformed.js", should be "../files/malformed.js"'], }), test({ code: 'require("./../files/malformed")', output: 'require("../files/malformed")', options: [{ commonjs: true }], - errors: [ 'Useless path segments for "./../files/malformed", should be "../files/malformed"'], + errors: ['Useless path segments for "./../files/malformed", should be "../files/malformed"'], }), test({ code: 'require("../files/malformed.js")', output: 'require("./malformed.js")', options: [{ commonjs: true }], - errors: [ 'Useless path segments for "../files/malformed.js", should be "./malformed.js"'], + errors: ['Useless path segments for "../files/malformed.js", should be "./malformed.js"'], }), test({ code: 'require("../files/malformed")', output: 'require("./malformed")', options: [{ commonjs: true }], - errors: [ 'Useless path segments for "../files/malformed", should be "./malformed"'], + errors: ['Useless path segments for "../files/malformed", should be "./malformed"'], }), test({ code: 'require("./test-module/")', output: 'require("./test-module")', options: [{ commonjs: true }], - errors: [ 'Useless path segments for "./test-module/", should be "./test-module"'], + errors: ['Useless path segments for "./test-module/", should be "./test-module"'], }), test({ code: 'require("./")', output: 'require(".")', options: [{ commonjs: true }], - errors: [ 'Useless path segments for "./", should be "."'], + errors: ['Useless path segments for "./", should be "."'], }), test({ code: 'require("../")', output: 'require("..")', options: [{ commonjs: true }], - errors: [ 'Useless path segments for "../", should be ".."'], + errors: ['Useless path segments for "../", should be ".."'], }), test({ code: 'require("./deep//a")', output: 'require("./deep/a")', options: [{ commonjs: true }], - errors: [ 'Useless path segments for "./deep//a", should be "./deep/a"'], + errors: ['Useless path segments for "./deep//a", should be "./deep/a"'], }), // CommonJS modules + noUselessIndex @@ -141,42 +141,42 @@ function runResolverTests(resolver) { test({ code: 'import "./../files/malformed.js"', output: 'import "../files/malformed.js"', - errors: [ 'Useless path segments for "./../files/malformed.js", should be "../files/malformed.js"'], + errors: ['Useless path segments for "./../files/malformed.js", should be "../files/malformed.js"'], }), test({ code: 'import "./../files/malformed"', output: 'import "../files/malformed"', - errors: [ 'Useless path segments for "./../files/malformed", should be "../files/malformed"'], + errors: ['Useless path segments for "./../files/malformed", should be "../files/malformed"'], }), test({ code: 'import "../files/malformed.js"', output: 'import "./malformed.js"', - errors: [ 'Useless path segments for "../files/malformed.js", should be "./malformed.js"'], + errors: ['Useless path segments for "../files/malformed.js", should be "./malformed.js"'], }), test({ code: 'import "../files/malformed"', output: 'import "./malformed"', - errors: [ 'Useless path segments for "../files/malformed", should be "./malformed"'], + errors: ['Useless path segments for "../files/malformed", should be "./malformed"'], }), test({ code: 'import "./test-module/"', output: 'import "./test-module"', - errors: [ 'Useless path segments for "./test-module/", should be "./test-module"'], + errors: ['Useless path segments for "./test-module/", should be "./test-module"'], }), test({ code: 'import "./"', output: 'import "."', - errors: [ 'Useless path segments for "./", should be "."'], + errors: ['Useless path segments for "./", should be "."'], }), test({ code: 'import "../"', output: 'import ".."', - errors: [ 'Useless path segments for "../", should be ".."'], + errors: ['Useless path segments for "../", should be ".."'], }), test({ code: 'import "./deep//a"', output: 'import "./deep/a"', - errors: [ 'Useless path segments for "./deep//a", should be "./deep/a"'], + errors: ['Useless path segments for "./deep//a", should be "./deep/a"'], }), // ES modules + noUselessIndex @@ -231,19 +231,19 @@ function runResolverTests(resolver) { test({ code: 'import("./")', output: 'import(".")', - errors: [ 'Useless path segments for "./", should be "."'], + errors: ['Useless path segments for "./", should be "."'], parser: parsers.BABEL_OLD, }), test({ code: 'import("../")', output: 'import("..")', - errors: [ 'Useless path segments for "../", should be ".."'], + errors: ['Useless path segments for "../", should be ".."'], parser: parsers.BABEL_OLD, }), test({ code: 'import("./deep//a")', output: 'import("./deep/a")', - errors: [ 'Useless path segments for "./deep//a", should be "./deep/a"'], + errors: ['Useless path segments for "./deep//a", should be "./deep/a"'], parser: parsers.BABEL_OLD, }), ], diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index 84b341e1b0..db6aec4fcf 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -236,7 +236,7 @@ ruleTester.run('order', rule, { import fs from 'fs'; import { add } from './helper';`, options: [{ - groups: [ 'unknown', 'builtin', 'external', 'parent', 'sibling', 'index' ], + groups: ['unknown', 'builtin', 'external', 'parent', 'sibling', 'index'], }], }), // Using unknown import types (e.g. using a resolver alias via babel) diff --git a/utils/ignore.js b/utils/ignore.js index e41d1e5a50..960538e706 100644 --- a/utils/ignore.js +++ b/utils/ignore.js @@ -20,7 +20,7 @@ function validExtensions(context) { function makeValidExtensionSet(settings) { // start with explicit JS-parsed extensions - const exts = new Set(settings['import/extensions'] || [ '.js' ]); + const exts = new Set(settings['import/extensions'] || ['.js']); // all alternate parser extensions are also valid if ('import/parsers' in settings) { From a6de522d1afb734becb6c478766e6c2ed6c873c6 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 27 Jul 2023 10:10:02 -0700 Subject: [PATCH 39/49] [Tests] `no-unused-modules`: properly skip in mocha 3 --- tests/src/rules/no-unused-modules.js | 30 +++++++++++++++------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/tests/src/rules/no-unused-modules.js b/tests/src/rules/no-unused-modules.js index 936123ab71..219d996b3a 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -1374,19 +1374,21 @@ describe('parser ignores prefixes like BOM and hashbang', () => { }); }); -describe('supports flat eslint', { skip: !FlatRuleTester }, () => { - const flatRuleTester = new FlatRuleTester(); - flatRuleTester.run('no-unused-modules', rule, { - valid: [{ - options: unusedExportsOptions, - code: 'import { o2 } from "./file-o"; export default () => 12', - filename: testFilePath('./no-unused-modules/file-a.js'), - }], - invalid: [{ - options: unusedExportsOptions, - code: 'export default () => 13', - filename: testFilePath('./no-unused-modules/file-f.js'), - errors: [error(`exported declaration 'default' not used within other modules`)], - }], +(FlatRuleTester ? describe : describe.skip)('supports flat eslint', () => { + it('passes', () => { + const flatRuleTester = new FlatRuleTester(); + flatRuleTester.run('no-unused-modules', rule, { + valid: [{ + options: unusedExportsOptions, + code: 'import { o2 } from "./file-o"; export default () => 12', + filename: testFilePath('./no-unused-modules/file-a.js'), + }], + invalid: [{ + options: unusedExportsOptions, + code: 'export default () => 13', + filename: testFilePath('./no-unused-modules/file-f.js'), + errors: [error(`exported declaration 'default' not used within other modules`)], + }], + }); }); }); From 3e1dd0b7b5af7dc2e5f54fc25534b8a944fd49c2 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 27 Jul 2023 10:47:14 -0700 Subject: [PATCH 40/49] [Fix] `no-unused-modules`: improve schema - allow empty arrays in `src` and `ignoreExports` - enforce uniqueness in `src` and `ignoreExport` lists - allow false/true combo on missingExports/unusedExports --- CHANGELOG.md | 1 + docs/rules/no-unused-modules.md | 2 +- src/rules/no-unused-modules.js | 39 ++++++++++----------------------- 3 files changed, 14 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7febb37111..c85317cca1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [`order`]: partial fix for [#2687] (thanks [@ljharb]) - [`no-duplicates`]: Detect across type and regular imports ([#2835], thanks [@benkrejci]) - [`extensions`]: handle `.` and `..` properly ([#2778], thanks [@benasher44]) + - [`no-unused-modules`]: improve schema (thanks [@ljharb]) ### Changed - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) diff --git a/docs/rules/no-unused-modules.md b/docs/rules/no-unused-modules.md index 5cd24bef41..a8e1a3c182 100644 --- a/docs/rules/no-unused-modules.md +++ b/docs/rules/no-unused-modules.md @@ -12,7 +12,7 @@ Reports: ### Usage -In order for this plugin to work, one of the options `missingExports` or `unusedExports` must be enabled (see "Options" section below). In the future, these options will be enabled by default (see https://github.com/import-js/eslint-plugin-import/issues/1324) +In order for this plugin to work, at least one of the options `missingExports` or `unusedExports` must be enabled (see "Options" section below). In the future, these options will be enabled by default (see https://github.com/import-js/eslint-plugin-import/issues/1324) Example: ``` diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index a2b6f4ea27..f3d56e4291 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -416,17 +416,16 @@ module.exports = { src: { description: 'files/paths to be analyzed (only for unused exports)', type: 'array', - minItems: 1, + uniqueItems: true, items: { type: 'string', minLength: 1, }, }, ignoreExports: { - description: - 'files/paths for which unused exports will not be reported (e.g module entry points)', + description: 'files/paths for which unused exports will not be reported (e.g module entry points)', type: 'array', - minItems: 1, + uniqueItems: true, items: { type: 'string', minLength: 1, @@ -441,37 +440,23 @@ module.exports = { type: 'boolean', }, }, - not: { - properties: { - unusedExports: { enum: [false] }, - missingExports: { enum: [false] }, - }, - }, - anyOf: [{ - not: { + anyOf: [ + { properties: { unusedExports: { enum: [true] }, + src: { + minItems: 1, + }, }, + required: ['unusedExports'], }, - required: ['missingExports'], - }, { - not: { + { properties: { missingExports: { enum: [true] }, }, + required: ['missingExports'], }, - required: ['unusedExports'], - }, { - properties: { - unusedExports: { enum: [true] }, - }, - required: ['unusedExports'], - }, { - properties: { - missingExports: { enum: [true] }, - }, - required: ['missingExports'], - }], + ], }], }, From d3aa4780e349a8af6466f13524c258544dac3d36 Mon Sep 17 00:00:00 2001 From: Jemi Salo Date: Wed, 26 Jul 2023 16:37:16 +0300 Subject: [PATCH 41/49] [Tests] `no-unused-modules`: document error reported on entire `export` statement --- tests/src/rules/no-unused-modules.js | 46 ++++++++++++++++++---------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/tests/src/rules/no-unused-modules.js b/tests/src/rules/no-unused-modules.js index 219d996b3a..80f0fee730 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -164,24 +164,38 @@ ruleTester.run('no-unused-modules', rule, { invalid: [ test({ options: unusedExportsOptions, - code: `import eslint from 'eslint' - import fileA from './file-a' - import { b } from './file-b' - import { c1, c2 } from './file-c' - import { d } from './file-d' - import { e } from './file-e' - import { e2 } from './file-e' - import { h2 } from './file-h' - import * as l from './file-l' - export * from './file-n' - export { default, o0, o3 } from './file-o' - export { p } from './file-p' - import s from './file-s'`, + code: ` + import eslint from 'eslint' + import fileA from './file-a' + import { b } from './file-b' + import { c1, c2 } from './file-c' + import { d } from './file-d' + import { e } from './file-e' + import { e2 } from './file-e' + import { h2 } from './file-h' + import * as l from './file-l' + export * from './file-n' + export { default, o0, o3 } from './file-o' + export { p } from './file-p' + import s from './file-s' + `, filename: testFilePath('./no-unused-modules/file-0.js'), errors: [ - error(`exported declaration 'default' not used within other modules`), - error(`exported declaration 'o0' not used within other modules`), - error(`exported declaration 'o3' not used within other modules`), + { + message: `exported declaration 'default' not used within other modules`, + line: 12, + column: 9, + }, + { + message: `exported declaration 'o0' not used within other modules`, + line: 12, + column: 9, + }, + { + message: `exported declaration 'o3' not used within other modules`, + line: 12, + column: 9, + }, error(`exported declaration 'p' not used within other modules`), ], }), From 90e2dfa8c8f2ebdef8dc5d19873b3134c8a916e0 Mon Sep 17 00:00:00 2001 From: Jemi Salo Date: Wed, 26 Jul 2023 16:38:09 +0300 Subject: [PATCH 42/49] [Fix] `no-unused-modules`: report error on binding instead of parent export --- CHANGELOG.md | 5 ++++- src/rules/no-unused-modules.js | 2 +- tests/src/rules/no-unused-modules.js | 6 +++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c85317cca1..2f9e67189c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,8 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange - [`order`]: partial fix for [#2687] (thanks [@ljharb]) - [`no-duplicates`]: Detect across type and regular imports ([#2835], thanks [@benkrejci]) - [`extensions`]: handle `.` and `..` properly ([#2778], thanks [@benasher44]) - - [`no-unused-modules`]: improve schema (thanks [@ljharb]) +- [`no-unused-modules`]: improve schema (thanks [@ljharb]) +- [`no-unused-modules`]: report error on binding instead of parent export ([#2842], thanks [@Chamion]) ### Changed - [Docs] [`no-duplicates`]: fix example schema ([#2684], thanks [@simmo]) @@ -1076,6 +1077,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2842]: https://github.com/import-js/eslint-plugin-import/pull/2842 [#2835]: https://github.com/import-js/eslint-plugin-import/pull/2835 [#2832]: https://github.com/import-js/eslint-plugin-import/pull/2832 [#2778]: https://github.com/import-js/eslint-plugin-import/pull/2778 @@ -1666,6 +1668,7 @@ for info on changes for earlier releases. [@bradzacher]: https://github.com/bradzacher [@brendo]: https://github.com/brendo [@brettz9]: https://github.com/brettz9 +[@Chamion]: https://github.com/Chamion [@charlessuh]: https://github.com/charlessuh [@charpeni]: https://github.com/charpeni [@cherryblossom000]: https://github.com/cherryblossom000 diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index f3d56e4291..c8a20f8c0e 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -931,7 +931,7 @@ module.exports = { }, ExportNamedDeclaration(node) { node.specifiers.forEach((specifier) => { - checkUsage(node, specifier.exported.name || specifier.exported.value); + checkUsage(specifier, specifier.exported.name || specifier.exported.value); }); forEachDeclarationIdentifier(node.declaration, (name) => { checkUsage(node, name); diff --git a/tests/src/rules/no-unused-modules.js b/tests/src/rules/no-unused-modules.js index 80f0fee730..77fb608ccb 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -184,17 +184,17 @@ ruleTester.run('no-unused-modules', rule, { { message: `exported declaration 'default' not used within other modules`, line: 12, - column: 9, + column: 18, }, { message: `exported declaration 'o0' not used within other modules`, line: 12, - column: 9, + column: 27, }, { message: `exported declaration 'o3' not used within other modules`, line: 12, - column: 9, + column: 31, }, error(`exported declaration 'p' not used within other modules`), ], From 70f24f1fefcf3da0f804d273fa3347e9471fbb77 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 27 Jul 2023 20:25:17 -0700 Subject: [PATCH 43/49] [Tests] allow WSL builds to fail, for now --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index dbeb0132d6..e50ab87d2a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -26,6 +26,7 @@ matrix: allow_failures: - nodejs_version: "4" # for eslint 5 + - configuration: WSL platform: - x86 From 703e9f9395ed8fe03c6c8c3c4cc04360d7df3d7e Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 27 Jul 2023 20:38:59 -0700 Subject: [PATCH 44/49] [Refactor] `no-duplicates`, `no-unused-modules`: use `flatMap` instead of `map` + `filter` --- src/rules/no-default-export.js | 18 ++++++++++-------- src/rules/no-duplicates.js | 11 +++++------ src/rules/no-restricted-paths.js | 11 ++++++----- src/rules/no-unused-modules.js | 6 ++---- tests/src/package.js | 3 +-- tests/src/rules/order.js | 2 +- 6 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/rules/no-default-export.js b/src/rules/no-default-export.js index 6e5a537485..dabbae543a 100644 --- a/src/rules/no-default-export.js +++ b/src/rules/no-default-export.js @@ -27,14 +27,16 @@ module.exports = { }, ExportNamedDeclaration(node) { - 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 }); - } else if (specifier.type === 'ExportSpecifier') { - context.report({ node, message: noAliasDefault(specifier), loc }); - } - }); + 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 }); + } else if (specifier.type === 'ExportSpecifier') { + context.report({ node, message: noAliasDefault(specifier), loc }); + } + }); }, }; }, diff --git a/src/rules/no-duplicates.js b/src/rules/no-duplicates.js index 2373202cb6..6b4f4d559e 100644 --- a/src/rules/no-duplicates.js +++ b/src/rules/no-duplicates.js @@ -1,6 +1,8 @@ import resolve from 'eslint-module-utils/resolve'; -import docsUrl from '../docsUrl'; import semver from 'semver'; +import flatMap from 'array.prototype.flatmap'; + +import docsUrl from '../docsUrl'; let typescriptPkg; try { @@ -51,7 +53,7 @@ function getFix(first, rest, sourceCode, context) { } const defaultImportNames = new Set( - [first, ...rest].map(getDefaultImportName).filter(Boolean), + flatMap([].concat(first, rest || []), (x) => getDefaultImportName(x) || []), ); // Bail if there are multiple different default import names – it's up to the @@ -62,10 +64,7 @@ function getFix(first, rest, sourceCode, context) { // Leave it to the user to handle comments. Also skip `import * as ns from // './foo'` imports, since they cannot be merged into another import. - const restWithoutComments = rest.filter((node) => !( - hasProblematicComments(node, sourceCode) - || hasNamespace(node) - )); + const restWithoutComments = rest.filter((node) => !hasProblematicComments(node, sourceCode) && !hasNamespace(node)); const specifiers = restWithoutComments .map((node) => { diff --git a/src/rules/no-restricted-paths.js b/src/rules/no-restricted-paths.js index bce9fd1a03..cd680a1946 100644 --- a/src/rules/no-restricted-paths.js +++ b/src/rules/no-restricted-paths.js @@ -77,9 +77,11 @@ module.exports = { const restrictedPaths = options.zones || []; const basePath = options.basePath || process.cwd(); const currentFilename = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); - const matchingZones = restrictedPaths.filter((zone) => [].concat(zone.target) - .map((target) => path.resolve(basePath, target)) - .some((targetPath) => isMatchingTargetPath(currentFilename, targetPath))); + const matchingZones = restrictedPaths.filter( + (zone) => [].concat(zone.target) + .map((target) => path.resolve(basePath, target)) + .some((targetPath) => isMatchingTargetPath(currentFilename, targetPath)), + ); function isMatchingTargetPath(filename, targetPath) { if (isGlob(targetPath)) { @@ -231,8 +233,7 @@ module.exports = { reportInvalidExceptions(validatorsWithInvalidExceptions, node); const applicableValidatorsForImportPathExcludingExceptions = applicableValidatorsForImportPath - .filter((validator) => validator.hasValidExceptions) - .filter((validator) => !validator.isPathException(absoluteImportPath)); + .filter((validator) => validator.hasValidExceptions && !validator.isPathException(absoluteImportPath)); reportImportsInRestrictedZone(applicableValidatorsForImportPathExcludingExceptions, node, importPath, zone.message); }); } diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index c8a20f8c0e..ecba3a19ce 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -177,7 +177,7 @@ const resolveFiles = (src, ignoreExports, context) => { // prepare list of source files, don't consider files from node_modules return new Set( - srcFileList.filter(({ filename }) => !isNodeModule(filename)).map(({ filename }) => filename), + flatMap(srcFileList, ({ filename }) => isNodeModule(filename) ? [] : filename), ); }; @@ -359,9 +359,7 @@ const fileIsInPkg = (file) => { }; const checkPkgFieldObject = (pkgField) => { - const pkgFieldFiles = values(pkgField) - .filter((value) => typeof value !== 'boolean') - .map((value) => join(basePath, value)); + const pkgFieldFiles = flatMap(values(pkgField), (value) => typeof value === 'boolean' ? [] : join(basePath, value)); if (includes(pkgFieldFiles, file)) { return true; diff --git a/tests/src/package.js b/tests/src/package.js index dd55e2740b..08138084c6 100644 --- a/tests/src/package.js +++ b/tests/src/package.js @@ -27,8 +27,7 @@ describe('package', function () { expect(err).not.to.exist; files.filter(isJSFile).forEach(function (f) { - expect(module.rules).to.have - .property(path.basename(f, '.js')); + expect(module.rules).to.have.property(path.basename(f, '.js')); }); done(); diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index db6aec4fcf..2a44aa06aa 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -2736,7 +2736,7 @@ ruleTester.run('order', rule, { }], }), ], - ].filter((t) => !!t), + ].filter(Boolean), }); context('TypeScript', function () { From 89f5d0d1b2c4200b90b45c37a588f08d59757187 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 27 Jul 2023 20:40:41 -0700 Subject: [PATCH 45/49] [Refactor] `no-anonymous-default-export`: use `fromEntries` instead of `reduce` --- src/rules/no-anonymous-default-export.js | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/rules/no-anonymous-default-export.js b/src/rules/no-anonymous-default-export.js index 06ea854b33..59a3cbfdac 100644 --- a/src/rules/no-anonymous-default-export.js +++ b/src/rules/no-anonymous-default-export.js @@ -60,16 +60,10 @@ const defs = { }, }; -const schemaProperties = Object.keys(defs) - .map((key) => defs[key]) - .reduce((acc, def) => { - acc[def.option] = { - description: def.description, - type: 'boolean', - }; - - return acc; - }, {}); +const schemaProperties = fromEntries(values(defs).map((def) => [def.option, { + description: def.description, + type: 'boolean', +}])); const defaults = fromEntries(values(defs).map((def) => [def.option, has(def, 'default') ? def.default : false])); From e7c248685eb1a74ad0e5093d5466f4de918c4cb6 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 27 Jul 2023 20:42:52 -0700 Subject: [PATCH 46/49] [Refactor] `no-useless-path-segments`: use `.filter` instead of `.reduce` --- src/rules/no-useless-path-segments.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/no-useless-path-segments.js b/src/rules/no-useless-path-segments.js index 343a4f6230..390a7546d3 100644 --- a/src/rules/no-useless-path-segments.js +++ b/src/rules/no-useless-path-segments.js @@ -33,7 +33,7 @@ function normalize(fn) { } function countRelativeParents(pathSegments) { - return pathSegments.reduce((sum, pathSegment) => pathSegment === '..' ? sum + 1 : sum, 0); + return pathSegments.filter((x) => x === '..').length; } module.exports = { From be928ae19461f405d813798e49d2968982823c17 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 27 Jul 2023 20:45:50 -0700 Subject: [PATCH 47/49] [Refactor] `no-internal-modules`: simplify a reduce --- src/rules/no-internal-modules.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/rules/no-internal-modules.js b/src/rules/no-internal-modules.js index 687193f5c5..5ed4565471 100644 --- a/src/rules/no-internal-modules.js +++ b/src/rules/no-internal-modules.js @@ -58,16 +58,14 @@ module.exports = { } function toSteps(somePath) { - return normalizeSep(somePath) + return normalizeSep(somePath) .split('/') + .filter((step) => step && step !== '.') .reduce((acc, step) => { - if (!step || step === '.') { - return acc; - } else if (step === '..') { + if (step === '..') { return acc.slice(0, -1); - } else { - return acc.concat(step); } + return acc.concat(step); }, []); } From 600fcc10dba3229e5f69373f6e2b0896032ad4b9 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 27 Jul 2023 20:49:06 -0700 Subject: [PATCH 48/49] [Refactor] `order`: use `object.groupby` --- package.json | 1 + src/rules/order.js | 9 ++------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index e870082e34..c214e59558 100644 --- a/package.json +++ b/package.json @@ -114,6 +114,7 @@ "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.6", + "object.groupby": "^1.0.0", "object.values": "^1.1.6", "resolve": "^1.22.3", "semver": "^6.3.1", diff --git a/src/rules/order.js b/src/rules/order.js index 27c3f4b0f9..6f70db263c 100644 --- a/src/rules/order.js +++ b/src/rules/order.js @@ -2,6 +2,7 @@ import minimatch from 'minimatch'; import includes from 'array-includes'; +import groupBy from 'object.groupby'; import importType from '../core/importType'; import isStaticRequire from '../core/staticRequire'; @@ -325,13 +326,7 @@ function getSorter(alphabetizeOptions) { } function mutateRanksToAlphabetize(imported, alphabetizeOptions) { - const groupedByRanks = imported.reduce(function (acc, importedItem) { - if (!Array.isArray(acc[importedItem.rank])) { - acc[importedItem.rank] = []; - } - acc[importedItem.rank].push(importedItem); - return acc; - }, {}); + const groupedByRanks = groupBy(imported, (item) => item.rank); const sorterFn = getSorter(alphabetizeOptions); From a257df9fe0683289c51e3ffe7b64ec0062fd69a8 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Thu, 27 Jul 2023 21:24:51 -0700 Subject: [PATCH 49/49] Bump to 2.28.0 --- CHANGELOG.md | 5 ++++- package.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f9e67189c..b7af776f86 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.28.0] - 2023-07-27 + ### Fixed - [`no-duplicates`]: remove duplicate identifiers in duplicate imports ([#2577], thanks [@joe-matsec]) - [`consistent-type-specifier-style`]: fix accidental removal of comma in certain cases ([#2754], thanks [@bradzacher]) @@ -1535,7 +1537,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.27.5...HEAD +[Unreleased]: https://github.com/import-js/eslint-plugin-import/compare/v2.28.0...HEAD +[2.28.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.27.5...v2.28.0 [2.27.5]: https://github.com/import-js/eslint-plugin-import/compare/v2.27.4...v2.27.5 [2.27.4]: https://github.com/import-js/eslint-plugin-import/compare/v2.27.3...v2.27.4 [2.27.3]: https://github.com/import-js/eslint-plugin-import/compare/v2.27.2...v2.27.3 diff --git a/package.json b/package.json index c214e59558..81905b59b2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-import", - "version": "2.27.5", + "version": "2.28.0", "description": "Import with sanity.", "engines": { "node": ">=4"