From 4a9be638407b8a01c5f70b077a407fc36805cdd7 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Mon, 16 Aug 2021 12:48:15 +0900 Subject: [PATCH 01/33] [Tests] use ESLint class in `cli` test --- tests/src/cli.js | 163 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 111 insertions(+), 52 deletions(-) diff --git a/tests/src/cli.js b/tests/src/cli.js index b0285593b..9867e0498 100644 --- a/tests/src/cli.js +++ b/tests/src/cli.js @@ -4,85 +4,144 @@ import path from 'path'; import { expect } from 'chai'; -import { CLIEngine } from 'eslint'; +import { CLIEngine, ESLint } from 'eslint'; import eslintPkg from 'eslint/package.json'; import semver from 'semver'; import * as importPlugin from '../../src/index'; describe('CLI regression tests', function () { describe('issue #210', function () { + let eslint; let cli; before(function () { - cli = new CLIEngine({ - useEslintrc: false, - configFile: './tests/files/issue210.config.js', - rulePaths: ['./src/rules'], - rules: { - 'named': 2, - }, - }); - cli.addPlugin('eslint-plugin-import', importPlugin); + if (ESLint) { + eslint = new ESLint({ + useEslintrc: false, + overrideConfigFile: './tests/files/issue210.config.js', + rulePaths: ['./src/rules'], + overrideConfig: { + rules: { + 'named': 2, + }, + }, + plugins: { 'eslint-plugin-import': importPlugin }, + }); + } else { + cli = new CLIEngine({ + useEslintrc: false, + configFile: './tests/files/issue210.config.js', + rulePaths: ['./src/rules'], + rules: { + 'named': 2, + }, + }); + cli.addPlugin('eslint-plugin-import', importPlugin); + } }); it("doesn't throw an error on gratuitous, erroneous self-reference", function () { - expect(() => cli.executeOnFiles(['./tests/files/issue210.js'])).not.to.throw(); + if (eslint) { + return eslint.lintFiles(['./tests/files/issue210.js']) + .catch(() => expect.fail()); + } else { + expect(() => cli.executeOnFiles(['./tests/files/issue210.js'])).not.to.throw(); + } }); }); describe('issue #1645', function () { + let eslint; let cli; beforeEach(function () { if (semver.satisfies(eslintPkg.version, '< 6')) { this.skip(); } else { - cli = new CLIEngine({ - useEslintrc: false, - configFile: './tests/files/just-json-files/.eslintrc.json', - rulePaths: ['./src/rules'], - ignore: false, - }); - cli.addPlugin('eslint-plugin-import', importPlugin); + if (ESLint) { + eslint = new ESLint({ + useEslintrc: false, + overrideConfigFile: './tests/files/just-json-files/.eslintrc.json', + rulePaths: ['./src/rules'], + ignore: false, + plugins: { 'eslint-plugin-import': importPlugin }, + }); + } else { + cli = new CLIEngine({ + useEslintrc: false, + configFile: './tests/files/just-json-files/.eslintrc.json', + rulePaths: ['./src/rules'], + ignore: false, + }); + cli.addPlugin('eslint-plugin-import', importPlugin); + } } }); it('throws an error on invalid JSON', () => { const invalidJSON = './tests/files/just-json-files/invalid.json'; - const results = cli.executeOnFiles([invalidJSON]); - expect(results).to.eql({ - results: [ - { - filePath: path.resolve(invalidJSON), - messages: [ + if (eslint) { + return eslint.lintFiles([invalidJSON]).then(results => { + expect(results).to.eql( + [ { - column: 2, - endColumn: 3, - endLine: 1, - line: 1, - message: 'Expected a JSON object, array or literal.', - nodeType: results.results[0].messages[0].nodeType, // we don't care about this one - ruleId: 'json/*', - severity: 2, - source: results.results[0].messages[0].source, // NewLine-characters might differ depending on git-settings + filePath: path.resolve(invalidJSON), + messages: [ + { + column: 2, + endColumn: 3, + endLine: 1, + line: 1, + message: 'Expected a JSON object, array or literal.', + nodeType: results[0].messages[0].nodeType, // we don't care about this one + ruleId: 'json/*', + severity: 2, + source: results[0].messages[0].source, // NewLine-characters might differ depending on git-settings + }, + ], + errorCount: 1, + ...(semver.satisfies(eslintPkg.version, '>= 7.32 || ^8.0.0-0') && { + fatalErrorCount: 0, + }), + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + source: results[0].source, // NewLine-characters might differ depending on git-settings + usedDeprecatedRules: results[0].usedDeprecatedRules, // we don't care about this one }, ], - errorCount: 1, - ...(semver.satisfies(eslintPkg.version, '>= 7.32') && { - fatalErrorCount: 0, - }), - warningCount: 0, - fixableErrorCount: 0, - fixableWarningCount: 0, - source: results.results[0].source, // NewLine-characters might differ depending on git-settings - }, - ], - ...(semver.satisfies(eslintPkg.version, '>= 7.32') && { - fatalErrorCount: 0, - }), - errorCount: 1, - warningCount: 0, - fixableErrorCount: 0, - fixableWarningCount: 0, - usedDeprecatedRules: results.usedDeprecatedRules, // we don't care about this one - }); + ); + }); + } else { + const results = cli.executeOnFiles([invalidJSON]); + expect(results).to.eql({ + results: [ + { + filePath: path.resolve(invalidJSON), + messages: [ + { + column: 2, + endColumn: 3, + endLine: 1, + line: 1, + message: 'Expected a JSON object, array or literal.', + nodeType: results.results[0].messages[0].nodeType, // we don't care about this one + ruleId: 'json/*', + severity: 2, + source: results.results[0].messages[0].source, // NewLine-characters might differ depending on git-settings + }, + ], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + source: results.results[0].source, // NewLine-characters might differ depending on git-settings + }, + ], + errorCount: 1, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + usedDeprecatedRules: results.usedDeprecatedRules, // we don't care about this one + }); + } }); }); }); From 49ada27a495899d0247bc9661ed0f055e970fe3b Mon Sep 17 00:00:00 2001 From: coderaiser Date: Mon, 16 Aug 2021 16:52:28 +0300 Subject: [PATCH 02/33] [New] `no-unused-modules`: add eslint v8 support - https://eslint.org/docs/8.0.0/user-guide/migrating-to-8.0.0 --- CHANGELOG.md | 4 +++ src/rules/no-unused-modules.js | 66 +++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 355985a81..cc7dca959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ## [Unreleased] +### Added +- [`no-unused-modules`]: add eslint v8 support ([#2194], thanks [@coderaiser]) + ## [2.24.2] - 2021-08-24 ### Fixed @@ -901,6 +904,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md [#2196]: https://github.com/import-js/eslint-plugin-import/pull/2196 +[#2194]: https://github.com/import-js/eslint-plugin-import/pull/2194 [#2184]: https://github.com/import-js/eslint-plugin-import/pull/2184 [#2179]: https://github.com/import-js/eslint-plugin-import/pull/2179 [#2160]: https://github.com/import-js/eslint-plugin-import/pull/2160 diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index d49674e7e..dd258e94e 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -13,45 +13,55 @@ import readPkgUp from 'read-pkg-up'; import values from 'object.values'; import includes from 'array-includes'; -// eslint/lib/util/glob-util has been moved to eslint/lib/util/glob-utils with version 5.3 -// and has been moved to eslint/lib/cli-engine/file-enumerator in version 6 +let FileEnumerator; let listFilesToProcess; + try { - const FileEnumerator = require('eslint/lib/cli-engine/file-enumerator').FileEnumerator; + ({ FileEnumerator } = require('eslint/use-at-your-own-risk')); +} catch (e) { + try { + // has been moved to eslint/lib/cli-engine/file-enumerator in version 6 + ({ FileEnumerator } = require('eslint/lib/cli-engine/file-enumerator')); + } catch (e) { + try { + // eslint/lib/util/glob-util has been moved to eslint/lib/util/glob-utils with version 5.3 + const { listFilesToProcess: originalListFilesToProcess } = require('eslint/lib/util/glob-utils'); + + // Prevent passing invalid options (extensions array) to old versions of the function. + // https://github.com/eslint/eslint/blob/v5.16.0/lib/util/glob-utils.js#L178-L280 + // https://github.com/eslint/eslint/blob/v5.2.0/lib/util/glob-util.js#L174-L269 + listFilesToProcess = function (src, extensions) { + return originalListFilesToProcess(src, { + extensions: extensions, + }); + }; + } catch (e) { + 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()); + + return originalListFilesToProcess(patterns); + }; + } + } +} + +if (FileEnumerator) { listFilesToProcess = function (src, extensions) { const e = new FileEnumerator({ extensions: extensions, }); + return Array.from(e.iterateFiles(src), ({ filePath, ignored }) => ({ ignored, filename: filePath, })); }; -} catch (e1) { - // Prevent passing invalid options (extensions array) to old versions of the function. - // https://github.com/eslint/eslint/blob/v5.16.0/lib/util/glob-utils.js#L178-L280 - // https://github.com/eslint/eslint/blob/v5.2.0/lib/util/glob-util.js#L174-L269 - let originalListFilesToProcess; - try { - originalListFilesToProcess = require('eslint/lib/util/glob-utils').listFilesToProcess; - listFilesToProcess = function (src, extensions) { - return originalListFilesToProcess(src, { - extensions: extensions, - }); - }; - } catch (e2) { - originalListFilesToProcess = require('eslint/lib/util/glob-util').listFilesToProcess; - - listFilesToProcess = function (src, extensions) { - const patterns = src.reduce((carry, pattern) => { - return carry.concat(extensions.map((extension) => { - return /\*\*|\*\./.test(pattern) ? pattern : `${pattern}/**/*${extension}`; - })); - }, src.slice()); - - return originalListFilesToProcess(patterns); - }; - } } const EXPORT_DEFAULT_DECLARATION = 'ExportDefaultDeclaration'; From 1359e247b2368f8bb6bc0fefc70fabf6bc49b075 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Fri, 27 Aug 2021 21:37:12 -0700 Subject: [PATCH 03/33] [eslint] enable `object-shorthand`, `space-before-function-paren` --- .eslintrc | 10 +++ resolvers/webpack/index.js | 8 +-- resolvers/webpack/test/config.js | 10 +-- src/rules/default.js | 2 +- src/rules/dynamic-import-chunkname.js | 2 +- src/rules/export.js | 2 +- src/rules/exports-last.js | 4 +- src/rules/extensions.js | 2 +- src/rules/first.js | 4 +- src/rules/named.js | 2 +- src/rules/newline-after-import.js | 4 +- src/rules/no-absolute-path.js | 2 +- src/rules/no-amd.js | 2 +- src/rules/no-anonymous-default-export.js | 2 +- src/rules/no-commonjs.js | 2 +- src/rules/no-cycle.js | 2 +- src/rules/no-deprecated.js | 2 +- src/rules/no-duplicates.js | 4 +- src/rules/no-dynamic-require.js | 2 +- src/rules/no-extraneous-dependencies.js | 2 +- src/rules/no-mutable-exports.js | 2 +- src/rules/no-named-as-default-member.js | 2 +- src/rules/no-named-as-default.js | 2 +- src/rules/no-named-default.js | 2 +- src/rules/no-namespace.js | 2 +- src/rules/no-nodejs-modules.js | 2 +- src/rules/no-self-import.js | 2 +- src/rules/no-unresolved.js | 2 +- src/rules/no-unused-modules.js | 4 +- src/rules/no-webpack-loader-syntax.js | 2 +- src/rules/order.js | 30 ++++----- src/rules/prefer-default-export.js | 18 +++--- src/rules/unambiguous.js | 4 +- tests/src/core/eslintParser.js | 2 +- tests/src/core/getExports.js | 2 +- tests/src/core/importType.js | 50 +++++++-------- tests/src/core/parse.js | 4 +- tests/src/core/parseStubParser.js | 2 +- tests/src/core/resolve.js | 64 +++++++++---------- tests/src/rules/default.js | 26 ++++---- tests/src/rules/export.js | 4 +- tests/src/rules/max-dependencies.js | 6 +- tests/src/rules/named.js | 40 ++++++------ tests/src/rules/namespace.js | 8 +-- tests/src/rules/newline-after-import.js | 14 ++-- tests/src/rules/no-deprecated.js | 2 +- tests/src/rules/no-duplicates.js | 2 +- tests/src/rules/no-extraneous-dependencies.js | 2 +- tests/src/rules/no-internal-modules.js | 2 +- tests/src/rules/no-unused-modules.js | 28 ++++---- tests/src/rules/no-webpack-loader-syntax.js | 2 +- tests/src/rules/prefer-default-export.js | 2 +- tests/src/utils.js | 2 +- 53 files changed, 209 insertions(+), 199 deletions(-) diff --git a/.eslintrc b/.eslintrc index 146ac91d7..923b8fd08 100644 --- a/.eslintrc +++ b/.eslintrc @@ -31,6 +31,10 @@ "no-shadow": 1, "no-var": 2, "object-curly-spacing": [2, "always"], + "object-shorthand": ["error", "always", { + "ignoreConstructors": false, + "avoidQuotes": true, + }], "one-var": [2, "never"], "prefer-const": 2, "quotes": [2, "single", { @@ -38,6 +42,12 @@ "avoidEscape": true, }], "semi": [2, "always"], + "space-before-function-paren": ["error", { + "anonymous": "always", + "named": "never", + "asyncArrow": "always", + }], + "eslint-plugin/consistent-output": [ "error", "always", diff --git a/resolvers/webpack/index.js b/resolvers/webpack/index.js index c7a3fbcae..3411eb75e 100644 --- a/resolvers/webpack/index.js +++ b/resolvers/webpack/index.js @@ -163,7 +163,7 @@ exports.resolve = function (source, file, settings) { const MAX_CACHE = 10; const _cache = []; function getResolveSync(configPath, webpackConfig, cwd) { - const cacheKey = { configPath: configPath, webpackConfig: webpackConfig }; + const cacheKey = { configPath, webpackConfig }; let cached = find(_cache, function (entry) { return isEqual(entry.key, cacheKey); }); if (!cached) { cached = { @@ -265,7 +265,7 @@ function createWebpack1ResolveSync(webpackRequire, resolveConfig, plugins) { resolver.apply( resolveConfig.packageAlias ? new DirectoryDescriptionFileFieldAliasPlugin('package.json', resolveConfig.packageAlias) - : function() {}, + : function () {}, new ModuleAliasPlugin(resolveConfig.alias || {}), makeRootPlugin(ModulesInRootPlugin, 'module', resolveConfig.root), new ModulesInDirectoriesPlugin( @@ -302,7 +302,7 @@ function createWebpack1ResolveSync(webpackRequire, resolveConfig, plugins) { resolver.apply.apply(resolver, resolvePlugins); - return function() { + return function () { return resolver.resolveSync.apply(resolver, arguments); }; } @@ -391,7 +391,7 @@ function findExternal(source, externals, context, resolveSync) { } function findConfigPath(configPath, packageDir) { - const extensions = Object.keys(interpret.extensions).sort(function(a, b) { + const extensions = Object.keys(interpret.extensions).sort(function (a, b) { return a === '.js' ? -1 : b === '.js' ? 1 : a.length - b.length; }); let extension; diff --git a/resolvers/webpack/test/config.js b/resolvers/webpack/test/config.js index 069c2e394..35f46e66c 100644 --- a/resolvers/webpack/test/config.js +++ b/resolvers/webpack/test/config.js @@ -101,7 +101,7 @@ describe('config', function () { .and.equal(path.join(__dirname, 'files', 'some', 'goofy', 'path', 'foo.js')); }); - it('finds the config at option env when config is a function', function() { + it('finds the config at option env when config is a function', function () { const settings = { config: require(path.join(__dirname, './files/webpack.function.config.js')), env: { @@ -113,7 +113,7 @@ describe('config', function () { .and.equal(path.join(__dirname, 'files', 'some', 'goofy', 'path', 'bar.js')); }); - it('finds the config at option env when config is an array of functions', function() { + it('finds the config at option env when config is an array of functions', function () { const settings = { config: require(path.join(__dirname, './files/webpack.function.config.multiple.js')), env: { @@ -125,7 +125,7 @@ describe('config', function () { .and.equal(path.join(__dirname, 'files', 'some', 'goofy', 'path', 'bar.js')); }); - it('passes argv to config when it is a function', function() { + it('passes argv to config when it is a function', function () { const settings = { config: require(path.join(__dirname, './files/webpack.function.config.js')), argv: { @@ -137,7 +137,7 @@ describe('config', function () { .and.equal(path.join(__dirname, 'files', 'some', 'bar', 'bar.js')); }); - it('passes a default empty argv object to config when it is a function', function() { + it('passes a default empty argv object to config when it is a function', function () { const settings = { config: require(path.join(__dirname, './files/webpack.function.config.js')), argv: undefined, @@ -146,7 +146,7 @@ describe('config', function () { expect(function () { resolve('baz', file, settings); }).to.not.throw(Error); }); - it('prevents async config using', function() { + it('prevents async config using', function () { const settings = { config: require(path.join(__dirname, './files/webpack.config.async.js')), }; diff --git a/src/rules/default.js b/src/rules/default.js index 797519a64..b8277b452 100644 --- a/src/rules/default.js +++ b/src/rules/default.js @@ -10,7 +10,7 @@ module.exports = { schema: [], }, - create: function (context) { + create(context) { function checkDefault(specifierType, node) { diff --git a/src/rules/dynamic-import-chunkname.js b/src/rules/dynamic-import-chunkname.js index 16cd85a4f..859e9fea4 100644 --- a/src/rules/dynamic-import-chunkname.js +++ b/src/rules/dynamic-import-chunkname.js @@ -24,7 +24,7 @@ module.exports = { }], }, - create: function (context) { + create(context) { const config = context.options[0]; const { importFunctions = [] } = config || {}; const { webpackChunknameFormat = '[0-9a-zA-Z-_/.]+' } = config || {}; diff --git a/src/rules/export.js b/src/rules/export.js index 386211baa..be75fa07f 100644 --- a/src/rules/export.js +++ b/src/rules/export.js @@ -54,7 +54,7 @@ module.exports = { schema: [], }, - create: function (context) { + create(context) { const namespace = new Map([[rootProgram, new Map()]]); function addNamed(name, node, parent, isType) { diff --git a/src/rules/exports-last.js b/src/rules/exports-last.js index ea044f32b..e89aa7eef 100644 --- a/src/rules/exports-last.js +++ b/src/rules/exports-last.js @@ -15,9 +15,9 @@ module.exports = { schema: [], }, - create: function (context) { + create(context) { return { - Program: function ({ body }) { + Program({ body }) { const lastNonExportStatementIndex = body.reduce(function findLastIndex(acc, item, index) { if (isNonExportStatement(item)) { return index; diff --git a/src/rules/extensions.js b/src/rules/extensions.js index 921b981e0..e116d2f86 100644 --- a/src/rules/extensions.js +++ b/src/rules/extensions.js @@ -103,7 +103,7 @@ module.exports = { }, }, - create: function (context) { + create(context) { const props = buildProperties(context); diff --git a/src/rules/first.js b/src/rules/first.js index 93e9fc553..eb613d330 100644 --- a/src/rules/first.js +++ b/src/rules/first.js @@ -21,8 +21,8 @@ module.exports = { ], }, - create: function (context) { - function isPossibleDirective (node) { + create(context) { + function isPossibleDirective(node) { return node.type === 'ExpressionStatement' && node.expression.type === 'Literal' && typeof node.expression.value === 'string'; diff --git a/src/rules/named.js b/src/rules/named.js index 2770d51fa..24e6bc0ac 100644 --- a/src/rules/named.js +++ b/src/rules/named.js @@ -21,7 +21,7 @@ module.exports = { ], }, - create: function (context) { + create(context) { const options = context.options[0] || {}; function checkSpecifiers(key, type, node) { diff --git a/src/rules/newline-after-import.js b/src/rules/newline-after-import.js index cf5dce831..1b946b35a 100644 --- a/src/rules/newline-after-import.js +++ b/src/rules/newline-after-import.js @@ -72,7 +72,7 @@ module.exports = { }, ], }, - create: function (context) { + create(context) { let level = 0; const requireCalls = []; @@ -138,7 +138,7 @@ after ${type} statement not followed by another ${type}.`, return { ImportDeclaration: checkImport, TSImportEqualsDeclaration: checkImport, - CallExpression: function(node) { + CallExpression(node) { if (isStaticRequire(node) && level === 0) { requireCalls.push(node); } diff --git a/src/rules/no-absolute-path.js b/src/rules/no-absolute-path.js index cc81c5c4b..4111fd7e6 100644 --- a/src/rules/no-absolute-path.js +++ b/src/rules/no-absolute-path.js @@ -11,7 +11,7 @@ module.exports = { schema: [ makeOptionsSchema() ], }, - create: function (context) { + create(context) { function reportIfAbsolute(source) { if (typeof source.value === 'string' && isAbsolute(source.value)) { context.report(source, 'Do not import modules using an absolute path'); diff --git a/src/rules/no-amd.js b/src/rules/no-amd.js index 7a0771bd5..187273589 100644 --- a/src/rules/no-amd.js +++ b/src/rules/no-amd.js @@ -18,7 +18,7 @@ module.exports = { schema: [], }, - create: function (context) { + create(context) { return { 'CallExpression': function (node) { if (context.getScope().type !== 'module') return; diff --git a/src/rules/no-anonymous-default-export.js b/src/rules/no-anonymous-default-export.js index 8ea336586..f1f495ca3 100644 --- a/src/rules/no-anonymous-default-export.js +++ b/src/rules/no-anonymous-default-export.js @@ -86,7 +86,7 @@ module.exports = { ], }, - create: function (context) { + create(context) { const options = Object.assign({}, defaults, context.options[0]); return { diff --git a/src/rules/no-commonjs.js b/src/rules/no-commonjs.js index 98f759cbc..9e157f46d 100644 --- a/src/rules/no-commonjs.js +++ b/src/rules/no-commonjs.js @@ -88,7 +88,7 @@ module.exports = { }, }, - create: function (context) { + create(context) { const options = normalizeLegacyOptions(context.options); return { diff --git a/src/rules/no-cycle.js b/src/rules/no-cycle.js index ec4cfeae9..dda5a679d 100644 --- a/src/rules/no-cycle.js +++ b/src/rules/no-cycle.js @@ -36,7 +36,7 @@ module.exports = { })], }, - create: function (context) { + create(context) { const myPath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); if (myPath === '') return {}; // can't cycle-check a non-file diff --git a/src/rules/no-deprecated.js b/src/rules/no-deprecated.js index 628759bd4..4913d389b 100644 --- a/src/rules/no-deprecated.js +++ b/src/rules/no-deprecated.js @@ -21,7 +21,7 @@ module.exports = { schema: [], }, - create: function (context) { + create(context) { const deprecated = new Map(); const namespaces = new Map(); diff --git a/src/rules/no-duplicates.js b/src/rules/no-duplicates.js index 6e03fcaef..c887b39e7 100644 --- a/src/rules/no-duplicates.js +++ b/src/rules/no-duplicates.js @@ -261,7 +261,7 @@ module.exports = { ], }, - create: function (context) { + create(context) { // Prepare the resolver from options. const considerQueryStringOption = context.options[0] && context.options[0]['considerQueryString']; @@ -300,7 +300,7 @@ module.exports = { } }, - 'Program:exit'() { + 'Program:exit': function () { checkImports(imported, context); checkImports(nsImported, context); checkImports(defaultTypesImported, context); diff --git a/src/rules/no-dynamic-require.js b/src/rules/no-dynamic-require.js index f8900088d..8267fd26e 100644 --- a/src/rules/no-dynamic-require.js +++ b/src/rules/no-dynamic-require.js @@ -38,7 +38,7 @@ module.exports = { ], }, - create: function (context) { + create(context) { const options = context.options[0] || {}; return { diff --git a/src/rules/no-extraneous-dependencies.js b/src/rules/no-extraneous-dependencies.js index 9403931f5..8d2e294cc 100644 --- a/src/rules/no-extraneous-dependencies.js +++ b/src/rules/no-extraneous-dependencies.js @@ -252,7 +252,7 @@ module.exports = { ], }, - create: function (context) { + create(context) { const options = context.options[0] || {}; const filename = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); const deps = getDependencies(context, options.packageDir) || extractDepFields({}); diff --git a/src/rules/no-mutable-exports.js b/src/rules/no-mutable-exports.js index a1635bb7a..c506c997c 100644 --- a/src/rules/no-mutable-exports.js +++ b/src/rules/no-mutable-exports.js @@ -9,7 +9,7 @@ module.exports = { schema: [], }, - create: function (context) { + create(context) { function checkDeclaration(node) { const { kind } = node; if (kind === 'var' || kind === 'let') { diff --git a/src/rules/no-named-as-default-member.js b/src/rules/no-named-as-default-member.js index 09bb5e34a..ef2000e22 100644 --- a/src/rules/no-named-as-default-member.js +++ b/src/rules/no-named-as-default-member.js @@ -21,7 +21,7 @@ module.exports = { schema: [], }, - create: function(context) { + create(context) { const fileImports = new Map(); const allPropertyLookups = new Map(); diff --git a/src/rules/no-named-as-default.js b/src/rules/no-named-as-default.js index 7313e6126..7c1ef0e04 100644 --- a/src/rules/no-named-as-default.js +++ b/src/rules/no-named-as-default.js @@ -11,7 +11,7 @@ module.exports = { schema: [], }, - create: function (context) { + create(context) { function checkDefault(nameKey, defaultSpecifier) { // #566: default is a valid specifier if (defaultSpecifier[nameKey].name === 'default') return; diff --git a/src/rules/no-named-default.js b/src/rules/no-named-default.js index d1c15d62e..116c89cf5 100644 --- a/src/rules/no-named-default.js +++ b/src/rules/no-named-default.js @@ -9,7 +9,7 @@ module.exports = { schema: [], }, - create: function (context) { + create(context) { return { 'ImportDeclaration': function (node) { node.specifiers.forEach(function (im) { diff --git a/src/rules/no-namespace.js b/src/rules/no-namespace.js index 472b93dd0..fb3d76052 100644 --- a/src/rules/no-namespace.js +++ b/src/rules/no-namespace.js @@ -32,7 +32,7 @@ module.exports = { }], }, - create: function (context) { + create(context) { const firstOption = context.options[0] || {}; const ignoreGlobs = firstOption.ignore; diff --git a/src/rules/no-nodejs-modules.js b/src/rules/no-nodejs-modules.js index cbfb384d3..1e3207d20 100644 --- a/src/rules/no-nodejs-modules.js +++ b/src/rules/no-nodejs-modules.js @@ -31,7 +31,7 @@ module.exports = { ], }, - create: function (context) { + create(context) { const options = context.options[0] || {}; const allowed = options.allow || []; diff --git a/src/rules/no-self-import.js b/src/rules/no-self-import.js index 58c393b66..a5f464b24 100644 --- a/src/rules/no-self-import.js +++ b/src/rules/no-self-import.js @@ -30,7 +30,7 @@ module.exports = { schema: [], }, - create: function (context) { + create(context) { return moduleVisitor((source, node) => { isImportingSelf(context, node, source.value); }, { commonjs: true }); diff --git a/src/rules/no-unresolved.js b/src/rules/no-unresolved.js index 719bbded9..236692b6e 100644 --- a/src/rules/no-unresolved.js +++ b/src/rules/no-unresolved.js @@ -22,7 +22,7 @@ module.exports = { ], }, - create: function (context) { + create(context) { function checkSourceValue(source) { const shouldCheckCase = !CASE_SENSITIVE_FS && (!context.options[0] || context.options[0].caseSensitive !== false); diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index dd258e94e..ae6646f87 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -32,7 +32,7 @@ try { // https://github.com/eslint/eslint/blob/v5.2.0/lib/util/glob-util.js#L174-L269 listFilesToProcess = function (src, extensions) { return originalListFilesToProcess(src, { - extensions: extensions, + extensions, }); }; } catch (e) { @@ -54,7 +54,7 @@ try { if (FileEnumerator) { listFilesToProcess = function (src, extensions) { const e = new FileEnumerator({ - extensions: extensions, + extensions, }); return Array.from(e.iterateFiles(src), ({ filePath, ignored }) => ({ diff --git a/src/rules/no-webpack-loader-syntax.js b/src/rules/no-webpack-loader-syntax.js index b3228e0d2..2f49ad6c6 100644 --- a/src/rules/no-webpack-loader-syntax.js +++ b/src/rules/no-webpack-loader-syntax.js @@ -18,7 +18,7 @@ module.exports = { schema: [], }, - create: function (context) { + create(context) { return moduleVisitor((source, node) => { reportIfNonStandard(context, node, source.value); }, { commonjs: true }); diff --git a/src/rules/order.js b/src/rules/order.js index 857a46e3d..2f4ef08b7 100644 --- a/src/rules/order.js +++ b/src/rules/order.js @@ -196,7 +196,7 @@ function fixOutOfOrder(context, firstNode, secondNode, order) { if (order === 'before') { context.report({ node: secondNode.node, - message: message, + message, fix: canFix && (fixer => fixer.replaceTextRange( [firstRootStart, secondRootEnd], @@ -206,7 +206,7 @@ function fixOutOfOrder(context, firstNode, secondNode, order) { } else if (order === 'after') { context.report({ node: secondNode.node, - message: message, + message, fix: canFix && (fixer => fixer.replaceTextRange( [secondRootStart, firstRootEnd], @@ -259,7 +259,7 @@ function getSorter(ascending) { } function mutateRanksToAlphabetize(imported, alphabetizeOptions) { - const groupedByRanks = imported.reduce(function(acc, importedItem) { + const groupedByRanks = imported.reduce(function (acc, importedItem) { if (!Array.isArray(acc[importedItem.rank])) { acc[importedItem.rank] = []; } @@ -275,14 +275,14 @@ function mutateRanksToAlphabetize(imported, alphabetizeOptions) { : (a, b) => sorterFn(a.value, b.value); // sort imports locally within their group - groupRanks.forEach(function(groupRank) { + groupRanks.forEach(function (groupRank) { groupedByRanks[groupRank].sort(comparator); }); // assign globally unique rank to each import let newRank = 0; - const alphabetizedRanks = groupRanks.sort().reduce(function(acc, groupRank) { - groupedByRanks[groupRank].forEach(function(importedItem) { + const alphabetizedRanks = groupRanks.sort().reduce(function (acc, groupRank) { + groupedByRanks[groupRank].forEach(function (importedItem) { acc[`${importedItem.value}|${importedItem.node.importKind}`] = parseInt(groupRank, 10) + newRank; newRank += 1; }); @@ -290,7 +290,7 @@ function mutateRanksToAlphabetize(imported, alphabetizeOptions) { }, {}); // mutate the original group-rank with alphabetized-rank - imported.forEach(function(importedItem) { + imported.forEach(function (importedItem) { importedItem.rank = alphabetizedRanks[`${importedItem.value}|${importedItem.node.importKind}`]; }); } @@ -359,11 +359,11 @@ 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) { - const rankObject = groups.reduce(function(res, group, index) { + const rankObject = groups.reduce(function (res, group, index) { if (typeof group === 'string') { group = [group]; } - group.forEach(function(groupItem) { + group.forEach(function (groupItem) { if (types.indexOf(groupItem) === -1) { throw new Error('Incorrect configuration of the rule: Unknown type `' + JSON.stringify(groupItem) + '`'); @@ -376,11 +376,11 @@ function convertGroupsToRanks(groups) { return res; }, {}); - const omittedTypes = types.filter(function(type) { + const omittedTypes = types.filter(function (type) { return rankObject[type] === undefined; }); - const ranks = omittedTypes.reduce(function(res, type) { + const ranks = omittedTypes.reduce(function (res, type) { res[type] = groups.length; return res; }, rankObject); @@ -457,7 +457,7 @@ function removeNewLineAfterImport(context, currentImport, previousImport) { return undefined; } -function makeNewlinesBetweenReport (context, imported, newlinesBetweenImports) { +function makeNewlinesBetweenReport(context, imported, newlinesBetweenImports) { const getNumberOfEmptyLinesBetween = (currentImport, previousImport) => { const linesBetweenImports = context.getSourceCode().lines.slice( previousImport.node.loc.end.line, @@ -468,7 +468,7 @@ function makeNewlinesBetweenReport (context, imported, newlinesBetweenImports) { }; let previousImport = imported[0]; - imported.slice(1).forEach(function(currentImport) { + imported.slice(1).forEach(function (currentImport) { const emptyLinesBetween = getNumberOfEmptyLinesBetween(currentImport, previousImport); if (newlinesBetweenImports === 'always' @@ -581,7 +581,7 @@ module.exports = { ], }, - create: function importOrderRule (context) { + create: function importOrderRule(context) { const options = context.options[0] || {}; const newlinesBetweenImports = options['newlines-between'] || 'ignore'; const pathGroupsExcludedImportTypes = new Set(options['pathGroupsExcludedImportTypes'] || ['builtin', 'external', 'object']); @@ -600,7 +600,7 @@ module.exports = { } catch (error) { // Malformed configuration return { - Program: function(node) { + Program(node) { context.report(node, error.message); }, }; diff --git a/src/rules/prefer-default-export.js b/src/rules/prefer-default-export.js index 3cd2224cd..a8c52bb1a 100644 --- a/src/rules/prefer-default-export.js +++ b/src/rules/prefer-default-export.js @@ -11,7 +11,7 @@ module.exports = { schema: [], }, - create: function(context) { + create(context) { let specifierExportCount = 0; let hasDefaultExport = false; let hasStarExport = false; @@ -22,7 +22,7 @@ module.exports = { if (identifierOrPattern && identifierOrPattern.type === 'ObjectPattern') { // recursively capture identifierOrPattern.properties - .forEach(function(property) { + .forEach(function (property) { captureDeclaration(property.value); }); } else if (identifierOrPattern && identifierOrPattern.type === 'ArrayPattern') { @@ -35,11 +35,11 @@ module.exports = { } return { - 'ExportDefaultSpecifier': function() { + 'ExportDefaultSpecifier': function () { hasDefaultExport = true; }, - 'ExportSpecifier': function(node) { + 'ExportSpecifier': function (node) { if (node.exported.name === 'default') { hasDefaultExport = true; } else { @@ -48,7 +48,7 @@ module.exports = { } }, - 'ExportNamedDeclaration': function(node) { + 'ExportNamedDeclaration': function (node) { // if there are specifiers, node.declaration should be null if (!node.declaration) return; @@ -66,7 +66,7 @@ module.exports = { } if (node.declaration.declarations) { - node.declaration.declarations.forEach(function(declaration) { + node.declaration.declarations.forEach(function (declaration) { captureDeclaration(declaration.id); }); } else { @@ -77,15 +77,15 @@ module.exports = { namedExportNode = node; }, - 'ExportDefaultDeclaration': function() { + 'ExportDefaultDeclaration': function () { hasDefaultExport = true; }, - 'ExportAllDeclaration': function() { + 'ExportAllDeclaration': function () { hasStarExport = true; }, - 'Program:exit': function() { + 'Program:exit': function () { if (specifierExportCount === 1 && !hasDefaultExport && !hasStarExport && !hasTypeExport) { context.report(namedExportNode, 'Prefer default export.'); } diff --git a/src/rules/unambiguous.js b/src/rules/unambiguous.js index c0570b066..576b3379e 100644 --- a/src/rules/unambiguous.js +++ b/src/rules/unambiguous.js @@ -15,14 +15,14 @@ module.exports = { schema: [], }, - create: function (context) { + create(context) { // ignore non-modules if (context.parserOptions.sourceType !== 'module') { return {}; } return { - Program: function (ast) { + Program(ast) { if (!isModule(ast)) { context.report({ node: ast, diff --git a/tests/src/core/eslintParser.js b/tests/src/core/eslintParser.js index 492b83ea4..f53a394de 100644 --- a/tests/src/core/eslintParser.js +++ b/tests/src/core/eslintParser.js @@ -1,5 +1,5 @@ module.exports = { - parseForESLint: function() { + parseForESLint() { return { ast: {}, }; diff --git a/tests/src/core/getExports.js b/tests/src/core/getExports.js index 74c8d9ee9..2c9f70d5b 100644 --- a/tests/src/core/getExports.js +++ b/tests/src/core/getExports.js @@ -13,7 +13,7 @@ import * as unambiguous from 'eslint-module-utils/unambiguous'; describe('ExportMap', function () { const fakeContext = Object.assign( semver.satisfies(eslintPkg.version, '>= 7.28') ? { - getFilename: function () { throw new Error('Should call getPhysicalFilename() instead of getFilename()'); }, + getFilename() { throw new Error('Should call getPhysicalFilename() instead of getFilename()'); }, getPhysicalFilename: getFilename, } : { getFilename, diff --git a/tests/src/core/importType.js b/tests/src/core/importType.js index 3a5000212..0dcd5266b 100644 --- a/tests/src/core/importType.js +++ b/tests/src/core/importType.js @@ -9,18 +9,18 @@ describe('importType(name)', function () { const context = testContext(); const pathToTestFiles = path.join(__dirname, '..', '..', 'files'); - it("should return 'absolute' for paths starting with a /", function() { + it("should return 'absolute' for paths starting with a /", function () { expect(importType('/', context)).to.equal('absolute'); expect(importType('/path', context)).to.equal('absolute'); expect(importType('/some/path', context)).to.equal('absolute'); }); - it("should return 'builtin' for node.js modules", function() { + it("should return 'builtin' for node.js modules", function () { expect(importType('fs', context)).to.equal('builtin'); expect(importType('path', context)).to.equal('builtin'); }); - it("should return 'external' for non-builtin modules without a relative path", function() { + it("should return 'external' for non-builtin modules without a relative path", function () { expect(importType('lodash', context)).to.equal('external'); expect(importType('async', context)).to.equal('external'); expect(importType('chalk', context)).to.equal('external'); @@ -29,7 +29,7 @@ describe('importType(name)', function () { expect(importType('lodash/fp', context)).to.equal('external'); }); - it("should return 'external' for scopes packages", function() { + it("should return 'external' for scopes packages", function () { expect(importType('@cycle/', context)).to.equal('external'); expect(importType('@cycle/core', context)).to.equal('external'); expect(importType('@cycle/dom', context)).to.equal('external'); @@ -38,7 +38,7 @@ describe('importType(name)', function () { expect(importType('@some-thing/something/some-directory/someModule.js', context)).to.equal('external'); }); - it("should return 'external' for external modules that redirect to its parent module using package.json", function() { + it("should return 'external' for external modules that redirect to its parent module using package.json", function () { expect(importType('eslint-import-test-order-redirect/module', context)).to.equal('external'); expect(importType('@eslint/import-test-order-redirect-scoped/module', context)).to.equal('external'); }); @@ -75,13 +75,13 @@ describe('importType(name)', function () { expect(importType('constants', pathContext)).to.equal('internal'); }); - it("should return 'parent' for internal modules that go through the parent", function() { + it("should return 'parent' for internal modules that go through the parent", function () { expect(importType('../foo', context)).to.equal('parent'); expect(importType('../../foo', context)).to.equal('parent'); expect(importType('../bar/foo', context)).to.equal('parent'); }); - it("should return 'sibling' for internal modules that are connected to one of the siblings", function() { + it("should return 'sibling' for internal modules that are connected to one of the siblings", function () { expect(importType('./foo', context)).to.equal('sibling'); expect(importType('./foo/bar', context)).to.equal('sibling'); expect(importType('./importType', context)).to.equal('sibling'); @@ -90,19 +90,19 @@ describe('importType(name)', function () { expect(importType('./importType/index.js', context)).to.equal('sibling'); }); - it("should return 'index' for sibling index file", function() { + it("should return 'index' for sibling index file", function () { expect(importType('.', context)).to.equal('index'); expect(importType('./', context)).to.equal('index'); expect(importType('./index', context)).to.equal('index'); expect(importType('./index.js', context)).to.equal('index'); }); - it("should return 'unknown' for any unhandled cases", function() { + it("should return 'unknown' for any unhandled cases", function () { expect(importType(' /malformed', context)).to.equal('unknown'); expect(importType(' foo', context)).to.equal('unknown'); }); - it("should return 'builtin' for additional core modules", function() { + it("should return 'builtin' for additional core modules", function () { // without extra config, should be marked external expect(importType('electron', context)).to.equal('external'); expect(importType('@org/foobar', context)).to.equal('external'); @@ -114,7 +114,7 @@ describe('importType(name)', function () { expect(importType('@org/foobar', scopedContext)).to.equal('builtin'); }); - it("should return 'builtin' for resources inside additional core modules", function() { + it("should return 'builtin' for resources inside additional core modules", function () { const electronContext = testContext({ 'import/core-modules': ['electron'] }); expect(importType('electron/some/path/to/resource.json', electronContext)).to.equal('builtin'); @@ -122,31 +122,31 @@ describe('importType(name)', function () { expect(importType('@org/foobar/some/path/to/resource.json', scopedContext)).to.equal('builtin'); }); - it("should return 'external' for module from 'node_modules' with default config", function() { + it("should return 'external' for module from 'node_modules' with default config", function () { expect(importType('resolve', context)).to.equal('external'); }); - it("should return 'internal' for module from 'node_modules' if 'node_modules' missed in 'external-module-folders'", function() { + it("should return 'internal' for module from 'node_modules' if 'node_modules' missed in 'external-module-folders'", function () { const foldersContext = testContext({ 'import/external-module-folders': [] }); expect(importType('chai', foldersContext)).to.equal('internal'); }); - it("should return 'internal' for module from 'node_modules' if its name matched 'internal-regex'", function() { + it("should return 'internal' for module from 'node_modules' if its name matched 'internal-regex'", function () { const foldersContext = testContext({ 'import/internal-regex': '^@org' }); expect(importType('@org/foobar', foldersContext)).to.equal('internal'); }); - it("should return 'external' for module from 'node_modules' if its name did not match 'internal-regex'", function() { + it("should return 'external' for module from 'node_modules' if its name did not match 'internal-regex'", function () { const foldersContext = testContext({ 'import/internal-regex': '^@bar' }); expect(importType('@org/foobar', foldersContext)).to.equal('external'); }); - it("should return 'external' for module from 'node_modules' if 'node_modules' contained in 'external-module-folders'", function() { + it("should return 'external' for module from 'node_modules' if 'node_modules' contained in 'external-module-folders'", function () { const foldersContext = testContext({ 'import/external-module-folders': ['node_modules'] }); expect(importType('resolve', foldersContext)).to.equal('external'); }); - it('returns "external" for a scoped symlinked module', function() { + it('returns "external" for a scoped symlinked module', function () { const foldersContext = testContext({ 'import/resolver': 'node', 'import/external-module-folders': ['node_modules'], @@ -157,7 +157,7 @@ describe('importType(name)', function () { // We're using Webpack resolver here since it resolves all symlinks, which means that // directory path will not contain node_modules/ but will point to the // actual directory inside 'files' instead - it('returns "external" for a scoped module from a symlinked directory which name is contained in "external-module-folders" (webpack resolver)', function() { + it('returns "external" for a scoped module from a symlinked directory which name is contained in "external-module-folders" (webpack resolver)', function () { const foldersContext = testContext({ 'import/resolver': 'webpack', 'import/external-module-folders': ['symlinked-module'], @@ -165,7 +165,7 @@ describe('importType(name)', function () { expect(importType('@test-scope/some-module', foldersContext)).to.equal('external'); }); - it('returns "internal" for a scoped module from a symlinked directory which incomplete name is contained in "external-module-folders" (webpack resolver)', function() { + it('returns "internal" for a scoped module from a symlinked directory which incomplete name is contained in "external-module-folders" (webpack resolver)', function () { const foldersContext_1 = testContext({ 'import/resolver': 'webpack', 'import/external-module-folders': ['symlinked-mod'], @@ -179,7 +179,7 @@ describe('importType(name)', function () { expect(importType('@test-scope/some-module', foldersContext_2)).to.equal('internal'); }); - it('returns "external" for a scoped module from a symlinked directory which partial path is contained in "external-module-folders" (webpack resolver)', function() { + it('returns "external" for a scoped module from a symlinked directory which partial path is contained in "external-module-folders" (webpack resolver)', function () { const originalFoldersContext = testContext({ 'import/resolver': 'webpack', 'import/external-module-folders': [], @@ -193,7 +193,7 @@ describe('importType(name)', function () { expect(importType('@test-scope/some-module', foldersContext)).to.equal('external'); }); - it('returns "internal" for a scoped module from a symlinked directory which partial path w/ incomplete segment is contained in "external-module-folders" (webpack resolver)', function() { + it('returns "internal" for a scoped module from a symlinked directory which partial path w/ incomplete segment is contained in "external-module-folders" (webpack resolver)', function () { const foldersContext_1 = testContext({ 'import/resolver': 'webpack', 'import/external-module-folders': ['files/symlinked-mod'], @@ -207,7 +207,7 @@ describe('importType(name)', function () { expect(importType('@test-scope/some-module', foldersContext_2)).to.equal('internal'); }); - it('returns "external" for a scoped module from a symlinked directory which partial path ending w/ slash is contained in "external-module-folders" (webpack resolver)', function() { + it('returns "external" for a scoped module from a symlinked directory which partial path ending w/ slash is contained in "external-module-folders" (webpack resolver)', function () { const foldersContext = testContext({ 'import/resolver': 'webpack', 'import/external-module-folders': ['symlinked-module/'], @@ -215,7 +215,7 @@ describe('importType(name)', function () { expect(importType('@test-scope/some-module', foldersContext)).to.equal('external'); }); - it('returns "internal" for a scoped module from a symlinked directory when "external-module-folders" contains an absolute path resembling directory‘s relative path (webpack resolver)', function() { + it('returns "internal" for a scoped module from a symlinked directory when "external-module-folders" contains an absolute path resembling directory‘s relative path (webpack resolver)', function () { const foldersContext = testContext({ 'import/resolver': 'webpack', 'import/external-module-folders': ['/symlinked-module'], @@ -223,7 +223,7 @@ describe('importType(name)', function () { expect(importType('@test-scope/some-module', foldersContext)).to.equal('internal'); }); - it('returns "external" for a scoped module from a symlinked directory which absolute path is contained in "external-module-folders" (webpack resolver)', function() { + it('returns "external" for a scoped module from a symlinked directory which absolute path is contained in "external-module-folders" (webpack resolver)', function () { const foldersContext = testContext({ 'import/resolver': 'webpack', 'import/external-module-folders': [testFilePath('symlinked-module')], @@ -231,7 +231,7 @@ describe('importType(name)', function () { expect(importType('@test-scope/some-module', foldersContext)).to.equal('external'); }); - it('`isExternalModule` works with windows directory separator', function() { + it('`isExternalModule` works with windows directory separator', function () { const context = testContext(); expect(isExternalModule('foo', {}, 'E:\\path\\to\\node_modules\\foo', context)).to.equal(true); expect(isExternalModule('foo', { diff --git a/tests/src/core/parse.js b/tests/src/core/parse.js index a9c9ba584..7344d94f2 100644 --- a/tests/src/core/parse.js +++ b/tests/src/core/parse.js @@ -30,7 +30,7 @@ describe('parse(content, { settings, ecmaFeatures })', function () { const parseSpy = sinon.spy(); const parserOptions = { ecmaFeatures: { jsx: true } }; parseStubParser.parse = parseSpy; - parse(path, content, { settings: {}, parserPath: parseStubParserPath, parserOptions: parserOptions }); + parse(path, content, { settings: {}, parserPath: parseStubParserPath, parserOptions }); expect(parseSpy.callCount, 'custom parser to be called once').to.equal(1); expect(parseSpy.args[0][0], 'custom parser to get content as its first argument').to.equal(content); expect(parseSpy.args[0][1], 'custom parser to get an object as its second argument').to.be.an('object'); @@ -66,7 +66,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: 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); }); diff --git a/tests/src/core/parseStubParser.js b/tests/src/core/parseStubParser.js index 9d17f0b04..4ed17d9dd 100644 --- a/tests/src/core/parseStubParser.js +++ b/tests/src/core/parseStubParser.js @@ -1,4 +1,4 @@ // this stub must be in a separate file to require from parse via moduleRequire module.exports = { - parse: function () {}, + parse() {}, }; diff --git a/tests/src/core/resolve.js b/tests/src/core/resolve.js index ccfe5f6c2..f9e9a1034 100644 --- a/tests/src/core/resolve.js +++ b/tests/src/core/resolve.js @@ -23,15 +23,15 @@ describe('resolve', function () { const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-v1' }); expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), )).to.equal(utils.testFilePath('./bar.jsx')); expect(resolve( '../files/exception' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('exception.js'); } }), + , Object.assign({}, testContext, { getFilename() { return utils.getFilename('exception.js'); } }), )).to.equal(undefined); expect(resolve( '../files/not-found' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('not-found.js'); } }), + , Object.assign({}, testContext, { getFilename() { return utils.getFilename('not-found.js'); } }), )).to.equal(undefined); }); @@ -39,15 +39,15 @@ describe('resolve', function () { const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-no-version' }); expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), )).to.equal(utils.testFilePath('./bar.jsx')); expect(resolve( '../files/exception' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('exception.js'); } }), + , Object.assign({}, testContext, { getFilename() { return utils.getFilename('exception.js'); } }), )).to.equal(undefined); expect(resolve( '../files/not-found' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('not-found.js'); } }), + , Object.assign({}, testContext, { getFilename() { return utils.getFilename('not-found.js'); } }), )).to.equal(undefined); }); @@ -59,12 +59,12 @@ describe('resolve', function () { }; expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), )).to.equal(utils.testFilePath('./bar.jsx')); testContextReports.length = 0; expect(resolve( '../files/exception' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('exception.js'); } }), + , Object.assign({}, 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'); @@ -72,7 +72,7 @@ describe('resolve', function () { testContextReports.length = 0; expect(resolve( '../files/not-found' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('not-found.js'); } }), + , Object.assign({}, testContext, { getFilename() { return utils.getFilename('not-found.js'); } }), )).to.equal(undefined); expect(testContextReports.length).to.equal(0); }); @@ -81,7 +81,7 @@ describe('resolve', function () { const testContext = utils.testContext({ 'import/resolver': [ './foo-bar-resolver-v2', './foo-bar-resolver-v1' ] }); expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), )).to.equal(utils.testFilePath('./bar.jsx')); }); @@ -89,7 +89,7 @@ describe('resolve', function () { const testContext = utils.testContext({ 'import/resolver': { './foo-bar-resolver-v2': {} } }); expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), )).to.equal(utils.testFilePath('./bar.jsx')); }); @@ -97,7 +97,7 @@ describe('resolve', function () { const testContext = utils.testContext({ 'import/resolver': [ { './foo-bar-resolver-v2': {} }, { './foo-bar-resolver-v1': {} } ] }); expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), )).to.equal(utils.testFilePath('./bar.jsx')); }); @@ -105,7 +105,7 @@ describe('resolve', function () { const testContext = utils.testContext({ 'import/resolver': { 'foo': {} } }); expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, testContext, { getFilename() { return utils.getFilename('foo.js'); } }), )).to.equal(utils.testFilePath('./bar.jsx')); }); @@ -118,7 +118,7 @@ describe('resolve', function () { testContextReports.length = 0; expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, 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'); @@ -134,7 +134,7 @@ describe('resolve', function () { }; testContextReports.length = 0; expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, 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`); @@ -157,7 +157,7 @@ describe('resolve', function () { }; expect(resolve( '../files/exception' - , Object.assign({}, testContext, { getFilename: function () { return utils.getFilename('exception.js'); } }), + , Object.assign({}, 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'); @@ -174,15 +174,15 @@ describe('resolve', function () { const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-v1' }); expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, 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: function () { return utils.getFilename('exception.js'); } }), + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }), )).to.equal(undefined); expect(resolve( '../files/not-found' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('not-found.js'); } }), + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('not-found.js'); } }), )).to.equal(undefined); }); @@ -190,15 +190,15 @@ describe('resolve', function () { const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-no-version' }); expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, 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: function () { return utils.getFilename('exception.js'); } }), + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('exception.js'); } }), )).to.equal(undefined); expect(resolve( '../files/not-found' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('not-found.js'); } }), + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('not-found.js'); } }), )).to.equal(undefined); }); @@ -210,12 +210,12 @@ describe('resolve', function () { }; expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, 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: function () { return utils.getFilename('exception.js'); } }), + , Object.assign({}, 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'); @@ -223,7 +223,7 @@ describe('resolve', function () { testContextReports.length = 0; expect(resolve( '../files/not-found' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('not-found.js'); } }), + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('not-found.js'); } }), )).to.equal(undefined); expect(testContextReports.length).to.equal(0); }); @@ -232,7 +232,7 @@ describe('resolve', 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: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }), )).to.equal(utils.testFilePath('./bar.jsx')); }); @@ -240,7 +240,7 @@ describe('resolve', function () { const testContext = utils.testContext({ 'import/resolver': { './foo-bar-resolver-v2': {} } }); expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }), )).to.equal(utils.testFilePath('./bar.jsx')); }); @@ -248,7 +248,7 @@ describe('resolve', 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: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }), )).to.equal(utils.testFilePath('./bar.jsx')); }); @@ -256,7 +256,7 @@ describe('resolve', function () { const testContext = utils.testContext({ 'import/resolver': { 'foo': {} } }); expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename() { return utils.getFilename('foo.js'); } }), )).to.equal(utils.testFilePath('./bar.jsx')); }); @@ -269,7 +269,7 @@ describe('resolve', function () { testContextReports.length = 0; expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, 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'); @@ -285,7 +285,7 @@ describe('resolve', function () { }; testContextReports.length = 0; expect(resolve( '../files/foo' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('foo.js'); } }), + , Object.assign({}, 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`); @@ -308,7 +308,7 @@ describe('resolve', function () { }; expect(resolve( '../files/exception' - , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('exception.js'); } }), + , Object.assign({}, 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'); diff --git a/tests/src/rules/default.js b/tests/src/rules/default.js index a861fff76..15101fc0c 100644 --- a/tests/src/rules/default.js +++ b/tests/src/rules/default.js @@ -160,7 +160,7 @@ context('TypeScript', function () { valid: [ test({ code: `import foobar from "./typescript-default"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -168,7 +168,7 @@ context('TypeScript', function () { }), test({ code: `import foobar from "./typescript-export-assign-default"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -176,7 +176,7 @@ context('TypeScript', function () { }), test({ code: `import foobar from "./typescript-export-assign-function"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -184,7 +184,7 @@ context('TypeScript', function () { }), test({ code: `import foobar from "./typescript-export-assign-mixed"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -192,7 +192,7 @@ context('TypeScript', function () { }), test({ code: `import foobar from "./typescript-export-assign-default-reexport"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -200,7 +200,7 @@ context('TypeScript', function () { }), test({ code: `import React from "./typescript-export-assign-default-namespace"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -211,7 +211,7 @@ context('TypeScript', function () { }), test({ code: `import Foo from "./typescript-export-as-default-namespace"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -222,7 +222,7 @@ context('TypeScript', function () { }), test({ code: `import Foo from "./typescript-export-react-test-renderer"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -233,7 +233,7 @@ context('TypeScript', function () { }), test({ code: `import foobar from "./typescript-export-assign-property"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -244,7 +244,7 @@ context('TypeScript', function () { invalid: [ test({ code: `import foobar from "./typescript"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -253,7 +253,7 @@ context('TypeScript', function () { }), test({ code: `import React from "./typescript-export-assign-default-namespace"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -262,7 +262,7 @@ context('TypeScript', function () { }), test({ code: `import FooBar from "./typescript-export-as-default-namespace"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -271,7 +271,7 @@ context('TypeScript', function () { }), test({ code: `import Foo from "./typescript-export-as-default-namespace"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, diff --git a/tests/src/rules/export.js b/tests/src/rules/export.js index d997b949b..efc6e402f 100644 --- a/tests/src/rules/export.js +++ b/tests/src/rules/export.js @@ -129,7 +129,7 @@ ruleTester.run('export', rule, { context('TypeScript', function () { getTSParsers().forEach((parser) => { const parserConfig = { - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -218,7 +218,7 @@ context('TypeScript', function () { export * as A from './named-export-collision/a'; export * as B from './named-export-collision/b'; `, - parser: parser, + parser, }), ]), diff --git a/tests/src/rules/max-dependencies.js b/tests/src/rules/max-dependencies.js index 1251af97e..fc3b541f5 100644 --- a/tests/src/rules/max-dependencies.js +++ b/tests/src/rules/max-dependencies.js @@ -97,7 +97,7 @@ context('TypeScript', { skip: semver.satisfies(eslintPkg.version, '>5.0.0') }, ( valid: [ test({ code: 'import type { x } from \'./foo\'; import { y } from \'./bar\';', - parser: parser, + parser, options: [{ max: 1, ignoreTypeImports: true, @@ -107,7 +107,7 @@ context('TypeScript', { skip: semver.satisfies(eslintPkg.version, '>5.0.0') }, ( invalid: [ test({ code: 'import type { x } from \'./foo\'; import type { y } from \'./bar\'', - parser: parser, + parser, options: [{ max: 1, }], @@ -118,7 +118,7 @@ context('TypeScript', { skip: semver.satisfies(eslintPkg.version, '>5.0.0') }, ( test({ code: 'import type { x } from \'./foo\'; import type { y } from \'./bar\'; import type { z } from \'./baz\'', - parser: parser, + parser, options: [{ max: 2, ignoreTypeImports: false, diff --git a/tests/src/rules/named.js b/tests/src/rules/named.js index 56babfa50..992baa0fd 100644 --- a/tests/src/rules/named.js +++ b/tests/src/rules/named.js @@ -362,8 +362,8 @@ context('TypeScript', function () { test({ code: `import {a} from './export-star-3/b';`, filename: testFilePath('./export-star-3/a.js'), - parser: parser, - settings: settings, + parser, + settings, }), ]; @@ -376,52 +376,52 @@ context('TypeScript', function () { valid.push( test({ code: `import { MyType } from "./${source}"`, - parser: parser, - settings: settings, + parser, + settings, }), test({ code: `import { Foo } from "./${source}"`, - parser: parser, - settings: settings, + parser, + settings, }), test({ code: `import { Bar } from "./${source}"`, - parser: parser, - settings: settings, + parser, + settings, }), test({ code: `import { getFoo } from "./${source}"`, - parser: parser, - settings: settings, + parser, + settings, }), test({ code: `import { MyEnum } from "./${source}"`, - parser: parser, - settings: settings, + parser, + settings, }), test({ code: ` import { MyModule } from "./${source}" MyModule.ModuleFunction() `, - parser: parser, - settings: settings, + parser, + settings, }), test({ code: ` import { MyNamespace } from "./${source}" MyNamespace.NSModule.NSModuleFunction() `, - parser: parser, - settings: settings, + parser, + settings, }), ); invalid.push( test({ code: `import { MissingType } from "./${source}"`, - parser: parser, - settings: settings, + parser, + settings, errors: [{ message: `MissingType not found in './${source}'`, type: 'Identifier', @@ -429,8 +429,8 @@ context('TypeScript', function () { }), test({ code: `import { NotExported } from "./${source}"`, - parser: parser, - settings: settings, + parser, + settings, errors: [{ message: `NotExported not found in './${source}'`, type: 'Identifier', diff --git a/tests/src/rules/namespace.js b/tests/src/rules/namespace.js index 802d5b440..826637b97 100644 --- a/tests/src/rules/namespace.js +++ b/tests/src/rules/namespace.js @@ -128,7 +128,7 @@ const valid = [ import * as foo from "./typescript-declare-nested" foo.bar.MyFunction() `, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -137,7 +137,7 @@ const valid = [ test({ code: `import { foobar } from "./typescript-declare-interface"`, - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -146,7 +146,7 @@ const valid = [ test({ code: 'export * from "typescript/lib/typescript.d"', - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, @@ -155,7 +155,7 @@ const valid = [ test({ code: 'export = function name() {}', - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, diff --git a/tests/src/rules/newline-after-import.js b/tests/src/rules/newline-after-import.js index 39f4c4291..a00e86900 100644 --- a/tests/src/rules/newline-after-import.js +++ b/tests/src/rules/newline-after-import.js @@ -184,7 +184,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { import { ExecaReturnValue } from 'execa'; import execa = require('execa'); `, - parser: parser, + parser, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { @@ -192,7 +192,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { import execa = require('execa'); import { ExecaReturnValue } from 'execa'; `, - parser: parser, + parser, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { @@ -201,7 +201,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { import execa = require('execa'); import { ExecbReturnValue } from 'execb'; `, - parser: parser, + parser, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { @@ -210,14 +210,14 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { import { ExecaReturnValue } from 'execa'; import execb = require('execb'); `, - parser: parser, + parser, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { code: ` export import a = obj;\nf(a); `, - parser: parser, + parser, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { @@ -228,7 +228,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { export import a2 = a; f(a); }`, - parser: parser, + parser, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, { @@ -239,7 +239,7 @@ ruleTester.run('newline-after-import', require('rules/newline-after-import'), { stub } `, - parser: parser, + parser, parserOptions: { ecmaVersion: 2015, sourceType: 'module' }, }, ]), diff --git a/tests/src/rules/no-deprecated.js b/tests/src/rules/no-deprecated.js index aa2aebedc..290946735 100644 --- a/tests/src/rules/no-deprecated.js +++ b/tests/src/rules/no-deprecated.js @@ -201,7 +201,7 @@ ruleTester.run('no-deprecated: hoisting', rule, { describe('TypeScript', function () { getTSParsers().forEach((parser) => { const parserConfig = { - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, diff --git a/tests/src/rules/no-duplicates.js b/tests/src/rules/no-duplicates.js index faf096ace..e550b63ce 100644 --- a/tests/src/rules/no-duplicates.js +++ b/tests/src/rules/no-duplicates.js @@ -415,7 +415,7 @@ import {x,y} from './foo' ], }); -context('TypeScript', function() { +context('TypeScript', function () { getNonDefaultParsers() .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) .forEach((parser) => { diff --git a/tests/src/rules/no-extraneous-dependencies.js b/tests/src/rules/no-extraneous-dependencies.js index 370cce7e3..c6abcd6e4 100644 --- a/tests/src/rules/no-extraneous-dependencies.js +++ b/tests/src/rules/no-extraneous-dependencies.js @@ -384,7 +384,7 @@ ruleTester.run('no-extraneous-dependencies', rule, { describe('TypeScript', { skip: semver.satisfies(eslintPkg.version, '^4') }, function () { getTSParsers().forEach((parser) => { const parserConfig = { - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, diff --git a/tests/src/rules/no-internal-modules.js b/tests/src/rules/no-internal-modules.js index 2bad32c46..2fee9f450 100644 --- a/tests/src/rules/no-internal-modules.js +++ b/tests/src/rules/no-internal-modules.js @@ -139,7 +139,7 @@ ruleTester.run('no-internal-modules', rule, { } } `, - parser: parser, + parser, }), ]), test({ diff --git a/tests/src/rules/no-unused-modules.js b/tests/src/rules/no-unused-modules.js index 17e52b411..837f7e575 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -963,31 +963,31 @@ context('TypeScript', function () { const a2: c = {}; const a3: d = {}; `, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), }), test({ options: unusedExportsTypescriptOptions, code: `export const b = 2;`, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-b.ts'), }), test({ options: unusedExportsTypescriptOptions, code: `export interface c {};`, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-c.ts'), }), test({ options: unusedExportsTypescriptOptions, code: `export type d = {};`, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-d.ts'), }), test({ options: unusedExportsTypescriptOptions, code: `export enum e { f };`, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-e.ts'), }), test({ @@ -1003,31 +1003,31 @@ context('TypeScript', function () { const a3: d = {}; const a4: typeof e = undefined; `, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-a-import-type.ts'), }), test({ options: unusedExportsTypescriptOptions, code: `export const b = 2;`, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-b-used-as-type.ts'), }), test({ options: unusedExportsTypescriptOptions, code: `export interface c {};`, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-c-used-as-type.ts'), }), test({ options: unusedExportsTypescriptOptions, code: `export type d = {};`, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-d-used-as-type.ts'), }), test({ options: unusedExportsTypescriptOptions, code: `export enum e { f };`, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-e-used-as-type.ts'), }), // Should also be valid when the exporting files are linted before the importing ones @@ -1060,7 +1060,7 @@ context('TypeScript', function () { test({ options: unusedExportsTypescriptOptions, code: `export const b = 2;`, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-b-unused.ts'), errors: [ error(`exported declaration 'b' not used within other modules`), @@ -1069,7 +1069,7 @@ context('TypeScript', function () { test({ options: unusedExportsTypescriptOptions, code: `export interface c {};`, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-c-unused.ts'), errors: [ error(`exported declaration 'c' not used within other modules`), @@ -1078,7 +1078,7 @@ context('TypeScript', function () { test({ options: unusedExportsTypescriptOptions, code: `export type d = {};`, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-d-unused.ts'), errors: [ error(`exported declaration 'd' not used within other modules`), @@ -1087,7 +1087,7 @@ context('TypeScript', function () { test({ options: unusedExportsTypescriptOptions, code: `export enum e { f };`, - parser: parser, + parser, filename: testFilePath('./no-unused-modules/typescript/file-ts-e-unused.ts'), errors: [ error(`exported declaration 'e' not used within other modules`), diff --git a/tests/src/rules/no-webpack-loader-syntax.js b/tests/src/rules/no-webpack-loader-syntax.js index 5ec848bc6..a8aa0dd2b 100644 --- a/tests/src/rules/no-webpack-loader-syntax.js +++ b/tests/src/rules/no-webpack-loader-syntax.js @@ -76,7 +76,7 @@ ruleTester.run('no-webpack-loader-syntax', rule, { context('TypeScript', function () { getTSParsers().forEach((parser) => { const parserConfig = { - parser: parser, + parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, 'import/resolver': { 'eslint-import-resolver-typescript': true }, diff --git a/tests/src/rules/prefer-default-export.js b/tests/src/rules/prefer-default-export.js index 34323fe53..36205b193 100644 --- a/tests/src/rules/prefer-default-export.js +++ b/tests/src/rules/prefer-default-export.js @@ -148,7 +148,7 @@ ruleTester.run('prefer-default-export', rule, { ], }); -context('TypeScript', function() { +context('TypeScript', function () { getNonDefaultParsers().forEach((parser) => { const parserConfig = { parser, diff --git a/tests/src/utils.js b/tests/src/utils.js index e805e9545..4d23af755 100644 --- a/tests/src/utils.js +++ b/tests/src/utils.js @@ -46,7 +46,7 @@ export function test(t) { } export function testContext(settings) { - return { getFilename: function () { return FILENAME; }, + return { getFilename() { return FILENAME; }, settings: settings || {} }; } From 397b6c5f4787e06f73dd4f6a120f79e59644cef1 Mon Sep 17 00:00:00 2001 From: Filipp Riabchun Date: Mon, 30 Aug 2021 20:05:53 +0200 Subject: [PATCH 04/33] [Tests] run pretest on CI Extracted from #2212 / #1660. --- .github/workflows/node-4+.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/node-4+.yml b/.github/workflows/node-4+.yml index b13e3b88c..d4035d267 100644 --- a/.github/workflows/node-4+.yml +++ b/.github/workflows/node-4+.yml @@ -81,6 +81,7 @@ jobs: node-version: ${{ matrix.node-version }} after_install: npm run copy-metafiles && ./tests/dep-time-travel.sh skip-ls-check: true + - run: npm run pretest - run: npm run tests-only - run: npm run coveralls From 2a0d207704ee8cb919242fb073b9cd132e088f57 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 30 Aug 2021 12:55:52 -0700 Subject: [PATCH 05/33] [Tests] coveralls -> codecov --- .github/workflows/node-4+.yml | 2 +- .github/workflows/packages.yml | 1 + .nycrc | 2 +- .travis.yml | 2 +- appveyor.yml | 47 ++++++++++++++++++++++++---------- package.json | 6 ++--- resolvers/node/package.json | 4 +-- resolvers/webpack/package.json | 5 +--- tests/dep-time-travel.sh | 2 +- 9 files changed, 42 insertions(+), 29 deletions(-) diff --git a/.github/workflows/node-4+.yml b/.github/workflows/node-4+.yml index d4035d267..f2498807c 100644 --- a/.github/workflows/node-4+.yml +++ b/.github/workflows/node-4+.yml @@ -83,7 +83,7 @@ jobs: skip-ls-check: true - run: npm run pretest - run: npm run tests-only - - run: npm run coveralls + - uses: codecov/codecov-action@v1 node: name: 'node 4+' diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 79bd1ce46..e7302faea 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -43,6 +43,7 @@ jobs: after_install: npm run copy-metafiles && ./tests/dep-time-travel.sh && cd ${{ matrix.package }} && npm install skip-ls-check: true - run: cd ${{ matrix.package }} && npm run tests-only + - uses: codecov/codecov-action@v1 packages: name: 'packages: all tests' diff --git a/.nycrc b/.nycrc index 8147f3871..20bcfca22 100644 --- a/.nycrc +++ b/.nycrc @@ -1,7 +1,7 @@ { "all": true, "check-coverage": false, - "reporter": ["text-summary", "text", "html", "json"], + "reporter": ["text-summary", "lcov", "text", "html", "json"], "require": [ "babel-register" ], diff --git a/.travis.yml b/.travis.yml index 583a41197..c289a39e4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,4 +36,4 @@ script: - npm run tests-only after_success: - - npm run coveralls + - bash <(curl -s https://codecov.io/bash) diff --git a/appveyor.yml b/appveyor.yml index de79234eb..c728414d4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,28 +5,31 @@ configuration: # Test against this version of Node.js environment: matrix: + - nodejs_version: "16" - nodejs_version: "14" - nodejs_version: "12" - nodejs_version: "10" - nodejs_version: "8" - # - nodejs_version: "6" - # - nodejs_version: "4" + # - nodejs_version: "6" + # - nodejs_version: "4" image: Visual Studio 2019 matrix: fast_finish: false exclude: - - configuration: WSL - nodejs_version: "10" - configuration: WSL nodejs_version: "8" + - configuration: WSL + nodejs_version: "6" + - configuration: WSL + nodejs_version: "4" - # allow_failures: - # - nodejs_version: "4" # for eslint 5 + allow_failures: + - nodejs_version: "4" # for eslint 5 -# platform: -# - x86 -# - x64 +platform: + - x86 + - x64 # Initialization scripts. (runs before repo cloning) init: @@ -35,14 +38,26 @@ init: if ($env:nodejs_version -eq "4") { $env:NPM_VERSION="3" } - if ($env:nodejs_version -in @("8", "10", "12")) { - $env:NPM_VERSION="6.14.5" + if ($env:nodejs_version -in @("8")) { + $env:NPM_VERSION="6" + } + if ($env:nodejs_version -in @("10", "12", "14", "16")) { + $env:NPM_VERSION="6" # TODO: use npm 7 + $env:NPM_CONFIG_LEGACY_PEER_DEPS="true" } - ps: >- + $env:ESLINT_VERSION="7"; if ([int]$env:nodejs_version -le 8) { $env:ESLINT_VERSION="6" } + if ([int]$env:nodejs_version -le 7) { + $env:ESLINT_VERSION="5" + } + if ([int]$env:nodejs_version -le 6) { + $env:ESLINT_VERSION="4" + } - ps: $env:WINDOWS_NYC_VERSION = "15.0.1" + - ps: $env:TRAVIS_NODE_VERSION = $env:nodejs_version # Add `ci`-command to `PATH` for running commands either using cmd or wsl depending on the configuration - ps: $env:PATH += ";$(Join-Path $(pwd) "scripts")" @@ -54,6 +69,8 @@ before_build: # Install dependencies - ci npm install + - ci npm run copy-metafiles + - bash ./tests/dep-time-travel.sh 2>&1 # fix symlinks - git config core.symlinks true @@ -99,9 +116,6 @@ test_script: $env:RESOLVERS = [string]::Join(";", $resolvers); - FOR %%G in ("%RESOLVERS:;=";"%") do ( pushd %%~G & ci npm test & popd ) -on_success: - - ci npm run coveralls - # Configuration-specific steps for: - matrix: @@ -127,6 +141,9 @@ for: } $env:RESOLVERS = [string]::Join(";", $resolvers); - IF DEFINED RESOLVERS FOR %%G in ("%RESOLVERS:;=";"%") do ( pushd %%~G & ci npm install --no-save nyc@%WINDOWS_NYC_VERSION% & popd ) + # TODO: enable codecov for native windows builds + # on_success: + # - codecov - matrix: only: - configuration: WSL @@ -136,5 +153,7 @@ for: - ps: $env:WSLENV += ":nodejs_version" - ps: wsl curl -sL 'https://deb.nodesource.com/setup_${nodejs_version}.x' `| sudo APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=1 -E bash - - wsl sudo DEBIAN_FRONTEND=noninteractive apt install -y nodejs + on_success: + - ci bash <(curl -s https://codecov.io/bash) build: on diff --git a/package.json b/package.json index dd7cbcb35..7537df99a 100644 --- a/package.json +++ b/package.json @@ -25,14 +25,13 @@ "watch": "npm run tests-only -- -- --watch", "pretest": "linklocal", "posttest": "eslint .", - "mocha": "cross-env BABEL_ENV=test nyc -s mocha", + "mocha": "cross-env BABEL_ENV=test nyc mocha", "tests-only": "npm run mocha tests/src", "test": "npm run tests-only", "test-compiled": "npm run prepublish && BABEL_ENV=testCompiled mocha --compilers js:babel-register tests/src", "test-all": "node --require babel-register ./scripts/testAll", "prepublishOnly": "safe-publish-latest && npm run build", - "prepublish": "not-in-publish || npm run prepublishOnly", - "coveralls": "nyc report --reporter lcovonly && coveralls < ./coverage/lcov.info" + "prepublish": "not-in-publish || npm run prepublishOnly" }, "repository": { "type": "git", @@ -69,7 +68,6 @@ "babel-register": "^6.26.0", "babylon": "^6.18.0", "chai": "^4.3.4", - "coveralls": "^3.1.1", "cross-env": "^4.0.0", "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0", "eslint-import-resolver-node": "file:./resolvers/node", diff --git a/resolvers/node/package.json b/resolvers/node/package.json index 5e7e9dea7..51a7d49cc 100644 --- a/resolvers/node/package.json +++ b/resolvers/node/package.json @@ -9,8 +9,7 @@ "scripts": { "prepublishOnly": "cp ../../{LICENSE,.npmrc} ./", "tests-only": "nyc mocha", - "test": "npm run tests-only", - "coveralls": "nyc report --reporter lcovonly && cd ../.. && coveralls < ./resolvers/node/coverage/lcov.info" + "test": "npm run tests-only" }, "repository": { "type": "git", @@ -35,7 +34,6 @@ }, "devDependencies": { "chai": "^3.5.0", - "coveralls": "^3.1.0", "mocha": "^3.5.3", "nyc": "^11.9.0" } diff --git a/resolvers/webpack/package.json b/resolvers/webpack/package.json index fd805af8b..bd6269e2f 100644 --- a/resolvers/webpack/package.json +++ b/resolvers/webpack/package.json @@ -6,9 +6,7 @@ "scripts": { "prepublishOnly": "cp ../../{LICENSE,.npmrc} ./", "tests-only": "nyc mocha -t 5s", - "test": "npm run tests-only", - "report": "nyc report --reporter=html", - "coveralls": "nyc report --reporter lcovonly && cd ../.. && coveralls < ./resolvers/webpack/coverage/lcov.info" + "test": "npm run tests-only" }, "files": [ "index.js", @@ -53,7 +51,6 @@ "babel-preset-es2015-argon": "latest", "babel-register": "^6.26.0", "chai": "^3.5.0", - "coveralls": "^3.1.0", "mocha": "^3.5.3", "nyc": "^11.9.0", "webpack": "https://gist.github.com/ljharb/9cdb687f3806f8e6cb8a365d0b7840eb" diff --git a/tests/dep-time-travel.sh b/tests/dep-time-travel.sh index d6f5aedfc..2e2459576 100755 --- a/tests/dep-time-travel.sh +++ b/tests/dep-time-travel.sh @@ -2,7 +2,7 @@ # expected: ESLINT_VERSION numeric env var -echo "installing ${ESLINT_VERSION}..." +echo "installing ${ESLINT_VERSION} in node ${TRAVIS_NODE_VERSION} with TS parser ${TS_PARSER:-default}..." export NPM_CONFIG_LEGACY_PEER_DEPS=true From a032b8345a0910de804dde0c10ce3de85b3b0978 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 31 Aug 2021 19:09:09 -0700 Subject: [PATCH 06/33] [Tests] run `npm run pretest` in travis --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c289a39e4..a4b5f2b19 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,7 @@ before_install: install: - 'npm install' - 'if [ -n "${ESLINT_VERSION}" ]; then ./tests/dep-time-travel.sh; fi' + - 'npm run pretest' script: - npm run tests-only From bb8eab92fb1fbd324e9b7d7c1ad4436f1cc5ac35 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 6 Sep 2021 09:59:47 -0700 Subject: [PATCH 07/33] [eslint] enable `keyword-spacing` --- .eslintrc | 11 ++++++++++- resolvers/webpack/index.js | 2 +- src/core/importType.js | 2 +- utils/module-require.js | 4 ++-- utils/resolve.js | 2 +- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.eslintrc b/.eslintrc index 923b8fd08..4064c571d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -25,6 +25,15 @@ "eqeqeq": [2, "allow-null"], "func-call-spacing": 2, "indent": [2, 2], + "keyword-spacing": ["error", { + before: true, + after: true, + overrides: { + return: { after: true }, + throw: { after: true }, + case: { after: true } + } + }], "max-len": [1, 99, 2], "no-cond-assign": [2, "always"], "no-return-assign": [2, "always"], @@ -47,7 +56,7 @@ "named": "never", "asyncArrow": "always", }], - + "eslint-plugin/consistent-output": [ "error", "always", diff --git a/resolvers/webpack/index.js b/resolvers/webpack/index.js index 3411eb75e..690c3b52a 100644 --- a/resolvers/webpack/index.js +++ b/resolvers/webpack/index.js @@ -84,7 +84,7 @@ exports.resolve = function (source, file, settings) { if (configPath) { try { webpackConfig = require(configPath); - } catch(e) { + } catch (e) { console.log('Error resolving webpackConfig', e); throw e; } diff --git a/src/core/importType.js b/src/core/importType.js index 8457c7853..6e4ac9a4d 100644 --- a/src/core/importType.js +++ b/src/core/importType.js @@ -75,7 +75,7 @@ export function isScopedMain(name) { } function isRelativeToParent(name) { - return/^\.\.$|^\.\.[\\/]/.test(name); + return /^\.\.$|^\.\.[\\/]/.test(name); } const indexFiles = ['.', './', './index', './index.js']; diff --git a/utils/module-require.js b/utils/module-require.js index 70e551062..c03671ce5 100644 --- a/utils/module-require.js +++ b/utils/module-require.js @@ -18,12 +18,12 @@ exports.default = function moduleRequire(p) { const eslintPath = require.resolve('eslint'); const eslintModule = createModule(eslintPath); return require(Module._resolveFilename(p, eslintModule)); - } catch(err) { /* ignore */ } + } catch (err) { /* ignore */ } try { // try relative to entry point return require.main.require(p); - } catch(err) { /* ignore */ } + } catch (err) { /* ignore */ } // finally, try from here return require(p); diff --git a/utils/resolve.js b/utils/resolve.js index f488ea798..803910332 100644 --- a/utils/resolve.js +++ b/utils/resolve.js @@ -42,7 +42,7 @@ function tryRequire(target, sourceFile) { } else { resolved = require.resolve(target); } - } catch(e) { + } catch (e) { // If the target does not exist then just return undefined return undefined; } From 7784948fc5024476f0861b7b8bb20603f28cf809 Mon Sep 17 00:00:00 2001 From: stropho <3704482+stropho@users.noreply.github.com> Date: Mon, 6 Sep 2021 13:22:12 +0200 Subject: [PATCH 08/33] [New] `no-restricted-paths`: add/restore glob pattern support Fixes #2123. --- CHANGELOG.md | 2 + docs/rules/no-restricted-paths.md | 50 ++++++++++++++- package.json | 1 + src/rules/no-restricted-paths.js | 88 ++++++++++++++++++++------ tests/src/rules/no-restricted-paths.js | 72 ++++++++++++++++++++- 5 files changed, 192 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc7dca959..198551583 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ### Added - [`no-unused-modules`]: add eslint v8 support ([#2194], thanks [@coderaiser]) +- [`no-restricted-paths`]: add/restore glob pattern support ([#2219], thanks [@stropho]) ## [2.24.2] - 2021-08-24 @@ -903,6 +904,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2219]: https://github.com/import-js/eslint-plugin-import/pull/2219 [#2196]: https://github.com/import-js/eslint-plugin-import/pull/2196 [#2194]: https://github.com/import-js/eslint-plugin-import/pull/2194 [#2184]: https://github.com/import-js/eslint-plugin-import/pull/2184 diff --git a/docs/rules/no-restricted-paths.md b/docs/rules/no-restricted-paths.md index bfcb9af23..c9390754e 100644 --- a/docs/rules/no-restricted-paths.md +++ b/docs/rules/no-restricted-paths.md @@ -9,8 +9,18 @@ In order to prevent such scenarios this rule allows you to define restricted zon This rule has one option. The option is an object containing the definition of all restricted `zones` and the optional `basePath` which is used to resolve relative paths within. The default value for `basePath` is the current working directory. -Each zone consists of the `target` path and a `from` path. The `target` is the path where the restricted imports should be applied. The `from` path defines the folder that is not allowed to be used in an import. An optional `except` may be defined for a zone, allowing exception paths that would otherwise violate the related `from`. Note that `except` is relative to `from` and cannot backtrack to a parent directory. -You may also specify an optional `message` for a zone, which will be displayed in case of the rule violation. + +Each zone consists of the `target` path, a `from` path, and an optional `except` and `message` attribute. +- `target` is the path where the restricted imports should be applied. It can be expressed by + - directory string path that matches all its containing files + - glob pattern matching all the targeted files +- `from` path defines the folder that is not allowed to be used in an import. It can be expressed by + - directory string path that matches all its containing files + - glob pattern matching all the files restricted to be imported +- `except` may be defined for a zone, allowing exception paths that would otherwise violate the related `from`. Note that it does not alter the behaviour of `target` in any way. + - in case `from` is a glob pattern, `except` must be an array of glob patterns as well + - in case `from` is a directory path, `except` is relative to `from` and cannot backtrack to a parent directory. +- `message` - will be displayed in case of the rule violation. ### Examples @@ -77,4 +87,40 @@ The following pattern is not considered a problem: ```js import b from './b' + +``` + +--------------- + +Given the following folder structure: + +``` +my-project +├── client + └── foo.js + └── sub-module + └── bar.js + └── baz.js + +``` + +and the current configuration is set to: + +``` +{ "zones": [ { + "target": "./tests/files/restricted-paths/client/!(sub-module)/**/*", + "from": "./tests/files/restricted-paths/client/sub-module/**/*", +} ] } +``` + +The following import is considered a problem in `my-project/client/foo.js`: + +```js +import a from './sub-module/baz' +``` + +The following import is not considered a problem in `my-project/client/sub-module/bar.js`: + +```js +import b from './baz' ``` diff --git a/package.json b/package.json index 7537df99a..b845b609d 100644 --- a/package.json +++ b/package.json @@ -107,6 +107,7 @@ "find-up": "^2.0.0", "has": "^1.0.3", "is-core-module": "^2.6.0", + "is-glob": "^4.0.1", "minimatch": "^3.0.4", "object.values": "^1.1.4", "pkg-up": "^2.0.0", diff --git a/src/rules/no-restricted-paths.js b/src/rules/no-restricted-paths.js index 058aa43ea..e5bc6bc85 100644 --- a/src/rules/no-restricted-paths.js +++ b/src/rules/no-restricted-paths.js @@ -2,6 +2,8 @@ import path from 'path'; import resolve from 'eslint-module-utils/resolve'; import moduleVisitor from 'eslint-module-utils/moduleVisitor'; +import isGlob from 'is-glob'; +import { Minimatch, default as minimatch } from 'minimatch'; import docsUrl from '../docsUrl'; import importType from '../core/importType'; @@ -56,6 +58,10 @@ module.exports = { const matchingZones = restrictedPaths.filter((zone) => { const targetPath = path.resolve(basePath, zone.target); + if (isGlob(targetPath)) { + return minimatch(currentFilename, targetPath); + } + return containsPath(currentFilename, targetPath); }); @@ -72,18 +78,59 @@ module.exports = { }); } - const zoneExceptions = matchingZones.map((zone) => { - const exceptionPaths = zone.except || []; - const absoluteFrom = path.resolve(basePath, zone.from); - const absoluteExceptionPaths = exceptionPaths.map((exceptionPath) => path.resolve(absoluteFrom, exceptionPath)); - const hasValidExceptionPaths = absoluteExceptionPaths - .every((absoluteExceptionPath) => isValidExceptionPath(absoluteFrom, absoluteExceptionPath)); + function reportInvalidExceptionGlob(node) { + context.report({ + node, + message: 'Restricted path exceptions must be glob patterns when`from` is a glob pattern', + }); + } + + const makePathValidator = (zoneFrom, zoneExcept = []) => { + const absoluteFrom = path.resolve(basePath, zoneFrom); + const isGlobPattern = isGlob(zoneFrom); + let isPathRestricted; + let hasValidExceptions; + let isPathException; + let reportInvalidException; + + if (isGlobPattern) { + const mm = new Minimatch(absoluteFrom); + isPathRestricted = (absoluteImportPath) => mm.match(absoluteImportPath); + + hasValidExceptions = zoneExcept.every(isGlob); + + if (hasValidExceptions) { + const exceptionsMm = zoneExcept.map((except) => new Minimatch(except)); + isPathException = (absoluteImportPath) => exceptionsMm.some((mm) => mm.match(absoluteImportPath)); + } + + reportInvalidException = reportInvalidExceptionGlob; + } else { + isPathRestricted = (absoluteImportPath) => containsPath(absoluteImportPath, absoluteFrom); + + const absoluteExceptionPaths = zoneExcept + .map((exceptionPath) => path.resolve(absoluteFrom, exceptionPath)); + hasValidExceptions = absoluteExceptionPaths + .every((absoluteExceptionPath) => isValidExceptionPath(absoluteFrom, absoluteExceptionPath)); + + if (hasValidExceptions) { + isPathException = (absoluteImportPath) => absoluteExceptionPaths.some( + (absoluteExceptionPath) => containsPath(absoluteImportPath, absoluteExceptionPath), + ); + } + + reportInvalidException = reportInvalidExceptionPath; + } return { - absoluteExceptionPaths, - hasValidExceptionPaths, + isPathRestricted, + hasValidExceptions, + isPathException, + reportInvalidException, }; - }); + }; + + const validators = []; function checkForRestrictedImportPath(importPath, node) { const absoluteImportPath = resolve(importPath, context); @@ -93,22 +140,27 @@ module.exports = { } matchingZones.forEach((zone, index) => { - const absoluteFrom = path.resolve(basePath, zone.from); - - if (!containsPath(absoluteImportPath, absoluteFrom)) { - return; + if (!validators[index]) { + validators[index] = makePathValidator(zone.from, zone.except); } - const { hasValidExceptionPaths, absoluteExceptionPaths } = zoneExceptions[index]; + const { + isPathRestricted, + hasValidExceptions, + isPathException, + reportInvalidException, + } = validators[index]; - if (!hasValidExceptionPaths) { - reportInvalidExceptionPath(node); + if (!isPathRestricted(absoluteImportPath)) { return; } - const pathIsExcepted = absoluteExceptionPaths - .some((absoluteExceptionPath) => containsPath(absoluteImportPath, absoluteExceptionPath)); + if (!hasValidExceptions) { + reportInvalidException(node); + return; + } + const pathIsExcepted = isPathException(absoluteImportPath); if (pathIsExcepted) { return; } diff --git a/tests/src/rules/no-restricted-paths.js b/tests/src/rules/no-restricted-paths.js index 3ee728c5c..11934599e 100644 --- a/tests/src/rules/no-restricted-paths.js +++ b/tests/src/rules/no-restricted-paths.js @@ -14,6 +14,23 @@ ruleTester.run('no-restricted-paths', rule, { zones: [ { target: './tests/files/restricted-paths/server', from: './tests/files/restricted-paths/other' } ], } ], }), + test({ + code: 'import a from "../client/a.js"', + filename: testFilePath('./restricted-paths/server/b.js'), + options: [ { + zones: [ { target: '**/*', from: './tests/files/restricted-paths/other' } ], + } ], + }), + test({ + code: 'import a from "../client/a.js"', + filename: testFilePath('./restricted-paths/client/b.js'), + options: [ { + zones: [ { + target: './tests/files/restricted-paths/!(client)/**/*', + from: './tests/files/restricted-paths/client/**/*', + } ], + } ], + }), test({ code: 'const a = require("../client/a.js")', filename: testFilePath('./restricted-paths/server/b.js'), @@ -61,7 +78,17 @@ ruleTester.run('no-restricted-paths', rule, { } ], } ], }), - + test({ + code: 'import A from "../two/a.js"', + filename: testFilePath('./restricted-paths/server/one/a.js'), + options: [ { + zones: [ { + target: '**/*', + from: './tests/files/restricted-paths/server/**/*', + except: ['**/a.js'], + } ], + } ], + }), // irrelevant function calls test({ code: 'notrequire("../server/b.js")' }), @@ -93,6 +120,18 @@ ruleTester.run('no-restricted-paths', rule, { column: 15, } ], }), + test({ + code: 'import b from "../server/b.js"', + filename: testFilePath('./restricted-paths/client/a.js'), + options: [ { + zones: [ { target: './tests/files/restricted-paths/client/**/*', from: './tests/files/restricted-paths/server' } ], + } ], + errors: [ { + message: 'Unexpected path "../server/b.js" imported in restricted zone.', + line: 1, + column: 15, + } ], + }), test({ code: 'import a from "../client/a"\nimport c from "./c"', filename: testFilePath('./restricted-paths/server/b.js'), @@ -190,5 +229,36 @@ ruleTester.run('no-restricted-paths', rule, { column: 15, } ], }), + test({ + code: 'import A from "../two/a.js"', + filename: testFilePath('./restricted-paths/server/one/a.js'), + options: [ { + zones: [ { + target: '**/*', + from: './tests/files/restricted-paths/server/**/*', + } ], + } ], + errors: [ { + message: 'Unexpected path "../two/a.js" imported in restricted zone.', + line: 1, + column: 15, + } ], + }), + test({ + code: 'import A from "../two/a.js"', + filename: testFilePath('./restricted-paths/server/one/a.js'), + options: [ { + zones: [ { + target: '**/*', + from: './tests/files/restricted-paths/server/**/*', + except: ['a.js'], + } ], + } ], + errors: [ { + message: 'Restricted path exceptions must be glob patterns when`from` is a glob pattern', + line: 1, + column: 15, + } ], + }), ], }); From 2a8891ff63f1f0e01551eac1d379c88f4464a0cb Mon Sep 17 00:00:00 2001 From: Sergei Startsev Date: Mon, 23 Aug 2021 21:28:57 +0200 Subject: [PATCH 09/33] [utils] [New] `fileExistsWithCaseSync`: add `strict` argument See #1262. --- tests/src/core/resolve.js | 16 ++++++++++++---- utils/CHANGELOG.md | 5 +++++ utils/resolve.js | 6 +++--- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/tests/src/core/resolve.js b/tests/src/core/resolve.js index f9e9a1034..360d4a2e7 100644 --- a/tests/src/core/resolve.js +++ b/tests/src/core/resolve.js @@ -3,7 +3,6 @@ import eslintPkg from 'eslint/package.json'; import semver from 'semver'; import resolve, { CASE_SENSITIVE_FS, fileExistsWithCaseSync } from 'eslint-module-utils/resolve'; -import ModuleCache from 'eslint-module-utils/ModuleCache'; import * as path from 'path'; import * as fs from 'fs'; @@ -319,7 +318,11 @@ describe('resolve', function () { const caseDescribe = (!CASE_SENSITIVE_FS ? describe : describe.skip); caseDescribe('case sensitivity', function () { let file; - const testContext = utils.testContext({ 'import/resolve': { 'extensions': ['.jsx'] } }); + const testContext = utils.testContext({ + 'import/resolve': { 'extensions': ['.jsx'] }, + 'import/cache': { lifetime: 0 }, + }); + const testSettings = testContext.settings; before('resolve', function () { file = resolve( // Note the case difference 'MyUncoolComponent' vs 'MyUnCoolComponent' @@ -329,14 +332,19 @@ describe('resolve', function () { expect(file, 'path to ./jsx/MyUncoolComponent').to.exist; }); it('detects case does not match FS', function () { - expect(fileExistsWithCaseSync(file, ModuleCache.getSettings(testContext))) + expect(fileExistsWithCaseSync(file, testSettings)) .to.be.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, ModuleCache.getSettings(testContext), true)) + expect(fileExistsWithCaseSync(f, testSettings)) .to.be.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; + }); }); describe('rename cache correctness', function () { diff --git a/utils/CHANGELOG.md b/utils/CHANGELOG.md index 916005514..f33001b6f 100644 --- a/utils/CHANGELOG.md +++ b/utils/CHANGELOG.md @@ -5,6 +5,9 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ## Unreleased +### Added +- `fileExistsWithCaseSync`: add `strict` argument ([#1262], thanks [@sergei-startsev]) + ## v2.6.2 - 2021-08-08 ### Fixed @@ -102,6 +105,7 @@ Yanked due to critical issue with cache key resulting from #839. [#1409]: https://github.com/import-js/eslint-plugin-import/pull/1409 [#1356]: https://github.com/import-js/eslint-plugin-import/pull/1356 [#1290]: https://github.com/import-js/eslint-plugin-import/pull/1290 +[#1262]: https://github.com/import-js/eslint-plugin-import/pull/1262 [#1218]: https://github.com/import-js/eslint-plugin-import/pull/1218 [#1166]: https://github.com/import-js/eslint-plugin-import/issues/1166 [#1160]: https://github.com/import-js/eslint-plugin-import/pull/1160 @@ -119,6 +123,7 @@ Yanked due to critical issue with cache key resulting from #839. [@kaiyoma]: https://github.com/kaiyoma [@manuth]: https://github.com/manuth [@pmcelhaney]: https://github.com/pmcelhaney +[@sergei-startsev]: https://github.com/sergei-startsev [@sompylasar]: https://github.com/sompylasar [@timkraut]: https://github.com/timkraut [@vikr01]: https://github.com/vikr01 \ No newline at end of file diff --git a/utils/resolve.js b/utils/resolve.js index 803910332..27d5dcc1e 100644 --- a/utils/resolve.js +++ b/utils/resolve.js @@ -52,13 +52,13 @@ function tryRequire(target, sourceFile) { } // http://stackoverflow.com/a/27382838 -exports.fileExistsWithCaseSync = function fileExistsWithCaseSync(filepath, cacheSettings) { +exports.fileExistsWithCaseSync = function fileExistsWithCaseSync(filepath, cacheSettings, strict) { // don't care if the FS is case-sensitive if (CASE_SENSITIVE_FS) return true; // null means it resolved to a builtin if (filepath === null) return true; - if (filepath.toLowerCase() === process.cwd().toLowerCase()) return true; + if (filepath.toLowerCase() === process.cwd().toLowerCase() && !strict) return true; const parsedPath = path.parse(filepath); const dir = parsedPath.dir; @@ -73,7 +73,7 @@ exports.fileExistsWithCaseSync = function fileExistsWithCaseSync(filepath, cache if (filenames.indexOf(parsedPath.base) === -1) { result = false; } else { - result = fileExistsWithCaseSync(dir, cacheSettings); + result = fileExistsWithCaseSync(dir, cacheSettings, strict); } } fileExistsCache.set(filepath, result); From 35bd9773f6c2c4fafac25d184ae0a29b40b3caf3 Mon Sep 17 00:00:00 2001 From: Sergei Startsev Date: Mon, 23 Aug 2021 21:28:57 +0200 Subject: [PATCH 10/33] [New] `no-unresolved`: add `caseSensitiveStrict` option Fixes #1259. --- CHANGELOG.md | 2 ++ docs/rules/no-unresolved.md | 19 +++++++++++++++++-- src/rules/no-unresolved.js | 13 ++++++++----- tests/src/rules/no-unresolved.js | 29 ++++++++++++++++++++++++++++- 4 files changed, 55 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 198551583..426b1114a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ## [Unreleased] ### Added +- [`no-unresolved`]: add `caseSensitiveStrict` option ([#1262], thanks [@sergei-startsev]) - [`no-unused-modules`]: add eslint v8 support ([#2194], thanks [@coderaiser]) - [`no-restricted-paths`]: add/restore glob pattern support ([#2219], thanks [@stropho]) @@ -1053,6 +1054,7 @@ for info on changes for earlier releases. [#1294]: https://github.com/import-js/eslint-plugin-import/pull/1294 [#1290]: https://github.com/import-js/eslint-plugin-import/pull/1290 [#1277]: https://github.com/import-js/eslint-plugin-import/pull/1277 +[#1262]: https://github.com/import-js/eslint-plugin-import/pull/1262 [#1257]: https://github.com/import-js/eslint-plugin-import/pull/1257 [#1253]: https://github.com/import-js/eslint-plugin-import/pull/1253 [#1248]: https://github.com/import-js/eslint-plugin-import/pull/1248 diff --git a/docs/rules/no-unresolved.md b/docs/rules/no-unresolved.md index ae6177dfd..89d00b930 100644 --- a/docs/rules/no-unresolved.md +++ b/docs/rules/no-unresolved.md @@ -76,10 +76,25 @@ By default, this rule will report paths whose case do not match the underlying f const { default: x } = require('./foo') // reported if './foo' is actually './Foo' and caseSensitive: true ``` +#### `caseSensitiveStrict` + +The `caseSensitive` option does not detect case for the current working directory. The `caseSensitiveStrict` option allows checking `cwd` in resolved path. By default, the option is disabled. + + +```js +/*eslint import/no-unresolved: [2, { caseSensitiveStrict: true }]*/ + +// Absolute paths +import Foo from `/Users/fOo/bar/file.js` // reported, /Users/foo/bar/file.js +import Foo from `d:/fOo/bar/file.js` // reported, d:/foo/bar/file.js + +// Relative paths, cwd is Users/foo/ +import Foo from `./../fOo/bar/file.js` // reported +``` + ## When Not To Use It -If you're using a module bundler other than Node or Webpack, you may end up with -a lot of false positive reports of missing dependencies. +If you're using a module bundler other than Node or Webpack, you may end up with a lot of false positive reports of missing dependencies. ## Further Reading diff --git a/src/rules/no-unresolved.js b/src/rules/no-unresolved.js index 236692b6e..d7212560c 100644 --- a/src/rules/no-unresolved.js +++ b/src/rules/no-unresolved.js @@ -18,14 +18,17 @@ module.exports = { schema: [ makeOptionsSchema({ caseSensitive: { type: 'boolean', default: true }, + caseSensitiveStrict: { type: 'boolean', default: false }, }), ], }, create(context) { + const options = context.options[0] || {}; + function checkSourceValue(source) { - const shouldCheckCase = !CASE_SENSITIVE_FS - && (!context.options[0] || context.options[0].caseSensitive !== false); + const caseSensitive = !CASE_SENSITIVE_FS && options.caseSensitive !== false; + const caseSensitiveStrict = !CASE_SENSITIVE_FS && options.caseSensitiveStrict; const resolvedPath = resolve(source.value, context); @@ -34,9 +37,9 @@ module.exports = { source, `Unable to resolve path to module '${source.value}'.` ); - } else if (shouldCheckCase) { + } else if (caseSensitive || caseSensitiveStrict) { const cacheSettings = ModuleCache.getSettings(context.settings); - if (!fileExistsWithCaseSync(resolvedPath, cacheSettings)) { + if (!fileExistsWithCaseSync(resolvedPath, cacheSettings, caseSensitiveStrict)) { context.report( source, `Casing of ${source.value} does not match the underlying filesystem.` @@ -45,6 +48,6 @@ module.exports = { } } - return moduleVisitor(checkSourceValue, context.options[0]); + return moduleVisitor(checkSourceValue, options); }, }; diff --git a/tests/src/rules/no-unresolved.js b/tests/src/rules/no-unresolved.js index d21ee2fd8..19203074e 100644 --- a/tests/src/rules/no-unresolved.js +++ b/tests/src/rules/no-unresolved.js @@ -15,7 +15,7 @@ function runResolverTests(resolver) { function rest(specs) { specs.settings = Object.assign({}, specs.settings, - { 'import/resolver': resolver }, + { 'import/resolver': resolver, 'import/cache': { lifetime: 0 } }, ); return test(specs); @@ -227,6 +227,10 @@ function runResolverTests(resolver) { }); if (!CASE_SENSITIVE_FS) { + const relativePath = './tests/files/jsx/MyUnCoolComponent.jsx'; + const cwd = process.cwd(); + const mismatchedPath = path.join(cwd.toUpperCase(), relativePath).replace(/\\/g, '/'); + ruleTester.run('case sensitivity', rule, { valid: [ rest({ // test with explicit flag @@ -247,6 +251,29 @@ function runResolverTests(resolver) { }), ], }); + + ruleTester.run('case sensitivity strict', rule, { + valid: [ + // #1259 issue + rest({ // caseSensitiveStrict is disabled by default + code: `import foo from "${mismatchedPath}"`, + }), + ], + + invalid: [ + // #1259 issue + rest({ // test with enabled caseSensitiveStrict option + code: `import foo from "${mismatchedPath}"`, + options: [{ caseSensitiveStrict: true }], + errors: [`Casing of ${mismatchedPath} does not match the underlying filesystem.`], + }), + rest({ // test with enabled caseSensitiveStrict option and disabled caseSensitive + code: `import foo from "${mismatchedPath}"`, + options: [{ caseSensitiveStrict: true, caseSensitive: false }], + errors: [`Casing of ${mismatchedPath} does not match the underlying filesystem.`], + }), + ], + }); } } From 7579748b21f9cbdca84e917e65eb41b3794ed2d9 Mon Sep 17 00:00:00 2001 From: Max Komarychev Date: Sat, 20 Jun 2020 15:10:40 +0300 Subject: [PATCH 11/33] [utils] [new] add `visit`, to support dynamic imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See #1660, #2212. Co-authored-by: Max Komarychev Co-authored-by: Filipp Riabchun Co-authored-by: 薛定谔的猫 --- tests/src/core/getExports.js | 7 ++--- tests/src/core/parse.js | 1 - utils/CHANGELOG.md | 8 ++++- utils/parse.js | 57 +++++++++++++++++++++++++++++++++--- utils/unambiguous.js | 5 ++-- utils/visit.js | 24 +++++++++++++++ 6 files changed, 88 insertions(+), 14 deletions(-) create mode 100644 utils/visit.js diff --git a/tests/src/core/getExports.js b/tests/src/core/getExports.js index 2c9f70d5b..604ae5cf2 100644 --- a/tests/src/core/getExports.js +++ b/tests/src/core/getExports.js @@ -8,7 +8,7 @@ import ExportMap from '../../../src/ExportMap'; import * as fs from 'fs'; import { getFilename } from '../utils'; -import * as unambiguous from 'eslint-module-utils/unambiguous'; +import { test as testUnambiguous } from 'eslint-module-utils/unambiguous'; describe('ExportMap', function () { const fakeContext = Object.assign( @@ -438,7 +438,6 @@ describe('ExportMap', function () { // todo: move to utils describe('unambiguous regex', function () { - const testFiles = [ ['deep/b.js', true], ['bar.js', true], @@ -449,10 +448,8 @@ describe('ExportMap', function () { for (const [testFile, expectedRegexResult] of testFiles) { it(`works for ${testFile} (${expectedRegexResult})`, function () { const content = fs.readFileSync('./tests/files/' + testFile, 'utf8'); - expect(unambiguous.test(content)).to.equal(expectedRegexResult); + expect(testUnambiguous(content)).to.equal(expectedRegexResult); }); } - }); - }); diff --git a/tests/src/core/parse.js b/tests/src/core/parse.js index 7344d94f2..407070aa2 100644 --- a/tests/src/core/parse.js +++ b/tests/src/core/parse.js @@ -69,5 +69,4 @@ 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); }); - }); diff --git a/utils/CHANGELOG.md b/utils/CHANGELOG.md index f33001b6f..241a205b4 100644 --- a/utils/CHANGELOG.md +++ b/utils/CHANGELOG.md @@ -7,6 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ### Added - `fileExistsWithCaseSync`: add `strict` argument ([#1262], thanks [@sergei-startsev]) +- add `visit`, to support dynamic imports ([#1660], [#2212], thanks [@maxkomarychev], [@aladdin-add], [@Hypnosphi]) ## v2.6.2 - 2021-08-08 @@ -93,10 +94,12 @@ Yanked due to critical issue with cache key resulting from #839. ### Fixed - `unambiguous.test()` regex is now properly in multiline mode +[#2212]: https://github.com/import-js/eslint-plugin-import/pull/2212 [#2160]: https://github.com/import-js/eslint-plugin-import/pull/2160 [#2026]: https://github.com/import-js/eslint-plugin-import/pull/2026 [#1786]: https://github.com/import-js/eslint-plugin-import/pull/1786 [#1671]: https://github.com/import-js/eslint-plugin-import/pull/1671 +[#1660]: https://github.com/import-js/eslint-plugin-import/pull/1660 [#1606]: https://github.com/import-js/eslint-plugin-import/pull/1606 [#1602]: https://github.com/import-js/eslint-plugin-import/pull/1602 [#1591]: https://github.com/import-js/eslint-plugin-import/pull/1591 @@ -126,4 +129,7 @@ Yanked due to critical issue with cache key resulting from #839. [@sergei-startsev]: https://github.com/sergei-startsev [@sompylasar]: https://github.com/sompylasar [@timkraut]: https://github.com/timkraut -[@vikr01]: https://github.com/vikr01 \ No newline at end of file +[@vikr01]: https://github.com/vikr01 +[@maxkomarychev]: https://github.com/maxkomarychev +[@aladdin-add]: https://github.com/aladdin-add +[@Hypnosphi]: https://github.com/Hypnosphi \ No newline at end of file diff --git a/utils/parse.js b/utils/parse.js index 3b2ac028f..d1dd4ef03 100644 --- a/utils/parse.js +++ b/utils/parse.js @@ -3,9 +3,42 @@ exports.__esModule = true; const moduleRequire = require('./module-require').default; const extname = require('path').extname; +const fs = require('fs'); const log = require('debug')('eslint-plugin-import:parse'); +function getBabelVisitorKeys(parserPath) { + if (parserPath.endsWith('index.js')) { + const hypotheticalLocation = parserPath.replace('index.js', 'visitor-keys.js'); + if (fs.existsSync(hypotheticalLocation)) { + const keys = moduleRequire(hypotheticalLocation); + return keys.default || keys; + } + } else if (parserPath.endsWith('index.cjs')) { + const hypotheticalLocation = parserPath.replace('index.cjs', 'worker/ast-info.cjs'); + if (fs.existsSync(hypotheticalLocation)) { + const astInfo = moduleRequire(hypotheticalLocation); + return astInfo.getVisitorKeys(); + } + } + return null; +} + +function keysFromParser(parserPath, parserInstance, parsedResult) { + if (/.*espree.*/.test(parserPath)) { + return parserInstance.VisitorKeys; + } + if (/.*(babel-eslint|@babel\/eslint-parser).*/.test(parserPath)) { + return getBabelVisitorKeys(parserPath); + } + if (/.*@typescript-eslint\/parser/.test(parserPath)) { + if (parsedResult) { + return parsedResult.visitorKeys; + } + } + return null; +} + exports.default = function parse(path, content, context) { if (context == null) throw new Error('need context to parse properly'); @@ -45,20 +78,36 @@ exports.default = function parse(path, content, context) { if (typeof parser.parseForESLint === 'function') { let ast; try { - ast = parser.parseForESLint(content, parserOptions).ast; + const parserRaw = parser.parseForESLint(content, parserOptions); + ast = parserRaw.ast; + return { + ast, + visitorKeys: keysFromParser(parserPath, parser, parserRaw), + }; } catch (e) { console.warn(); console.warn('Error while parsing ' + parserOptions.filePath); console.warn('Line ' + e.lineNumber + ', column ' + e.column + ': ' + e.message); } if (!ast || typeof ast !== 'object') { - console.warn('`parseForESLint` from parser `' + parserPath + '` is invalid and will just be ignored'); + console.warn( + '`parseForESLint` from parser `' + + parserPath + + '` is invalid and will just be ignored', + ); } else { - return ast; + return { + ast, + visitorKeys: keysFromParser(parserPath, parser, undefined), + }; } } - return parser.parse(content, parserOptions); + const keys = keysFromParser(parserPath, parser, undefined); + return { + ast: parser.parse(content, parserOptions), + visitorKeys: keys, + }; }; function getParserPath(path, context) { diff --git a/utils/unambiguous.js b/utils/unambiguous.js index 1446632f3..75f21693b 100644 --- a/utils/unambiguous.js +++ b/utils/unambiguous.js @@ -1,8 +1,7 @@ 'use strict'; exports.__esModule = true; - -const pattern = /(^|;)\s*(export|import)((\s+\w)|(\s*[{*=]))/m; +const pattern = /(^|;)\s*(export|import)((\s+\w)|(\s*[{*=]))|import\(/m; /** * detect possible imports/exports without a full parse. * @@ -26,5 +25,5 @@ const unambiguousNodeType = /^(?:(?:Exp|Imp)ort.*Declaration|TSExportAssignment) * @return {Boolean} */ exports.isModule = function isUnambiguousModule(ast) { - return 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 new file mode 100644 index 000000000..77b09850a --- /dev/null +++ b/utils/visit.js @@ -0,0 +1,24 @@ +'use strict'; +exports.__esModule = true; + +exports.default = function visit(node, keys, visitorSpec) { + if (!node || !keys) { + return; + } + const type = node.type; + if (typeof visitorSpec[type] === 'function') { + visitorSpec[type](node); + } + const childFields = keys[type]; + if (!childFields) { + return; + } + childFields.forEach((fieldName) => { + [].concat(node[fieldName]).forEach((item) => { + visit(item, keys, visitorSpec); + }); + }); + if (typeof visitorSpec[`${type}:Exit`] === 'function') { + visitorSpec[`${type}:Exit`](node); + } +}; From 7c382f02a6cafe676ee754a896092040dad8daf6 Mon Sep 17 00:00:00 2001 From: Max Komarychev Date: Sat, 20 Jun 2020 15:10:40 +0300 Subject: [PATCH 12/33] [New] `no-unused-modules`: support dynamic imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All occurences of `import('...')` are treated as namespace imports (`import * as X from '...'`) See #1660, #2212. Co-authored-by: Max Komarychev Co-authored-by: Filipp Riabchun Co-authored-by: 薛定谔的猫 --- CHANGELOG.md | 4 + docs/rules/no-unused-modules.md | 2 +- src/ExportMap.js | 49 +++++++++++- src/rules/no-unused-modules.js | 36 ++++++++- .../no-unused-modules/dynamic-import-js-2.js | 13 ++++ .../no-unused-modules/dynamic-import-js.js | 5 ++ .../exports-for-dynamic-js-2.js | 5 ++ .../exports-for-dynamic-js.js | 5 ++ .../typescript/dynamic-import-ts.ts | 6 ++ .../typescript/exports-for-dynamic-ts.ts | 5 ++ tests/src/rules/no-unused-modules.js | 75 ++++++++++++++++++- 11 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 tests/files/no-unused-modules/dynamic-import-js-2.js create mode 100644 tests/files/no-unused-modules/dynamic-import-js.js create mode 100644 tests/files/no-unused-modules/exports-for-dynamic-js-2.js create mode 100644 tests/files/no-unused-modules/exports-for-dynamic-js.js create mode 100644 tests/files/no-unused-modules/typescript/dynamic-import-ts.ts create mode 100644 tests/files/no-unused-modules/typescript/exports-for-dynamic-ts.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 426b1114a..9203739fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [`no-unresolved`]: add `caseSensitiveStrict` option ([#1262], thanks [@sergei-startsev]) - [`no-unused-modules`]: add eslint v8 support ([#2194], thanks [@coderaiser]) - [`no-restricted-paths`]: add/restore glob pattern support ([#2219], thanks [@stropho]) +- [`no-unused-modules`]: support dynamic imports ([#1660], [#2212], thanks [@maxkomarychev], [@aladdin-add], [@Hypnosphi]) ## [2.24.2] - 2021-08-24 @@ -906,6 +907,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md [#2219]: https://github.com/import-js/eslint-plugin-import/pull/2219 +[#2212]: https://github.com/import-js/eslint-plugin-import/pull/2212 [#2196]: https://github.com/import-js/eslint-plugin-import/pull/2196 [#2194]: https://github.com/import-js/eslint-plugin-import/pull/2194 [#2184]: https://github.com/import-js/eslint-plugin-import/pull/2184 @@ -983,6 +985,7 @@ for info on changes for earlier releases. [#1676]: https://github.com/import-js/eslint-plugin-import/pull/1676 [#1666]: https://github.com/import-js/eslint-plugin-import/pull/1666 [#1664]: https://github.com/import-js/eslint-plugin-import/pull/1664 +[#1660]: https://github.com/import-js/eslint-plugin-import/pull/1660 [#1658]: https://github.com/import-js/eslint-plugin-import/pull/1658 [#1651]: https://github.com/import-js/eslint-plugin-import/pull/1651 [#1626]: https://github.com/import-js/eslint-plugin-import/pull/1626 @@ -1487,6 +1490,7 @@ for info on changes for earlier releases. [@MatthiasKunnen]: https://github.com/MatthiasKunnen [@mattijsbliek]: https://github.com/mattijsbliek [@Maxim-Mazurok]: https://github.com/Maxim-Mazurok +[@maxkomarychev]: https://github.com/maxkomarychev [@maxmalov]: https://github.com/maxmalov [@MikeyBeLike]: https://github.com/MikeyBeLike [@mplewis]: https://github.com/mplewis diff --git a/docs/rules/no-unused-modules.md b/docs/rules/no-unused-modules.md index 01c13557c..0bd805612 100644 --- a/docs/rules/no-unused-modules.md +++ b/docs/rules/no-unused-modules.md @@ -3,8 +3,8 @@ Reports: - modules without any exports - individual exports not being statically `import`ed or `require`ed from other modules in the same project + - dynamic imports are supported if argument is a literal string -Note: dynamic imports are currently not supported. ## Rule Details diff --git a/src/ExportMap.js b/src/ExportMap.js index 5bda83dd3..53091e466 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -7,6 +7,7 @@ import debug from 'debug'; import { SourceCode } from 'eslint'; import parse from 'eslint-module-utils/parse'; +import visit from 'eslint-module-utils/visit'; import resolve from 'eslint-module-utils/resolve'; import isIgnored, { hasValidExtension } from 'eslint-module-utils/ignore'; @@ -354,15 +355,57 @@ ExportMap.parse = function (path, content, context) { const isEsModuleInteropTrue = isEsModuleInterop(); let ast; + let visitorKeys; try { - ast = parse(path, content, context); + const result = parse(path, content, context); + ast = result.ast; + visitorKeys = result.visitorKeys; } catch (err) { - log('parse error:', path, err); m.errors.push(err); return m; // can't continue } - if (!unambiguous.isModule(ast)) return null; + m.visitorKeys = visitorKeys; + + let hasDynamicImports = false; + + function processDynamicImport(source) { + hasDynamicImports = true; + if (source.type !== 'Literal') { + return null; + } + const p = remotePath(source.value); + if (p == null) { + return null; + } + const importedSpecifiers = new Set(); + importedSpecifiers.add('ImportNamespaceSpecifier'); + const getter = thunkFor(p, context); + m.imports.set(p, { + getter, + declarations: new Set([{ + source: { + // capturing actual node reference holds full AST in memory! + value: source.value, + loc: source.loc, + }, + importedSpecifiers, + }]), + }); + } + + visit(ast, visitorKeys, { + ImportExpression(node) { + processDynamicImport(node.source); + }, + CallExpression(node) { + if (node.callee.type === 'Import') { + processDynamicImport(node.arguments[0]); + } + }, + }); + + if (!unambiguous.isModule(ast) && !hasDynamicImports) return null; const docstyle = (context.settings && context.settings['import/docstyle']) || ['jsdoc']; const docStyleParsers = {}; diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index ae6646f87..ab347bd8c 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -7,6 +7,7 @@ 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 'read-pkg-up'; @@ -154,6 +155,8 @@ const importList = new Map(); */ const exportList = new Map(); +const visitorKeyMap = new Map(); + const ignoredFiles = new Set(); const filesOutsideSrc = new Set(); @@ -193,8 +196,15 @@ const prepareImportsAndExports = (srcFiles, context) => { const imports = new Map(); const currentExports = Exports.get(file, context); if (currentExports) { - const { dependencies, reexports, imports: localImportList, namespace } = currentExports; - + const { + dependencies, + reexports, + imports: localImportList, + namespace, + visitorKeys, + } = currentExports; + + visitorKeyMap.set(file, visitorKeys); // dependencies === export * from const currentExportAll = new Set(); dependencies.forEach(getDependency => { @@ -675,6 +685,28 @@ module.exports = { }); }); + function processDynamicImport(source) { + if (source.type !== 'Literal') { + return null; + } + const p = resolve(source.value, context); + if (p == null) { + return null; + } + newNamespaceImports.add(p); + } + + visit(node, visitorKeyMap.get(file), { + ImportExpression(child) { + processDynamicImport(child.source); + }, + CallExpression(child) { + if (child.callee.type === 'Import') { + processDynamicImport(child.arguments[0]); + } + }, + }); + node.body.forEach(astNode => { let resolvedPath; diff --git a/tests/files/no-unused-modules/dynamic-import-js-2.js b/tests/files/no-unused-modules/dynamic-import-js-2.js new file mode 100644 index 000000000..3de28a65d --- /dev/null +++ b/tests/files/no-unused-modules/dynamic-import-js-2.js @@ -0,0 +1,13 @@ +const importPath = './exports-for-dynamic-js'; +class A { + method() { + const c = import(importPath) + } +} + + +class B { + method() { + const c = import('i-do-not-exist') + } +} diff --git a/tests/files/no-unused-modules/dynamic-import-js.js b/tests/files/no-unused-modules/dynamic-import-js.js new file mode 100644 index 000000000..36bf5c313 --- /dev/null +++ b/tests/files/no-unused-modules/dynamic-import-js.js @@ -0,0 +1,5 @@ +class A { + method() { + const c = import('./exports-for-dynamic-js') + } +} diff --git a/tests/files/no-unused-modules/exports-for-dynamic-js-2.js b/tests/files/no-unused-modules/exports-for-dynamic-js-2.js new file mode 100644 index 000000000..19082862f --- /dev/null +++ b/tests/files/no-unused-modules/exports-for-dynamic-js-2.js @@ -0,0 +1,5 @@ +export const a = 10; +export const b = 20; +export const c = 30; +const d = 40; +export default d; diff --git a/tests/files/no-unused-modules/exports-for-dynamic-js.js b/tests/files/no-unused-modules/exports-for-dynamic-js.js new file mode 100644 index 000000000..06d938e40 --- /dev/null +++ b/tests/files/no-unused-modules/exports-for-dynamic-js.js @@ -0,0 +1,5 @@ +export const a = 10 +export const b = 20 +export const c = 30 +const d = 40 +export default d diff --git a/tests/files/no-unused-modules/typescript/dynamic-import-ts.ts b/tests/files/no-unused-modules/typescript/dynamic-import-ts.ts new file mode 100644 index 000000000..10a17c3b1 --- /dev/null +++ b/tests/files/no-unused-modules/typescript/dynamic-import-ts.ts @@ -0,0 +1,6 @@ +class A { + method() { + const c = import('./exports-for-dynamic-ts') + } +} + diff --git a/tests/files/no-unused-modules/typescript/exports-for-dynamic-ts.ts b/tests/files/no-unused-modules/typescript/exports-for-dynamic-ts.ts new file mode 100644 index 000000000..566eb7c7d --- /dev/null +++ b/tests/files/no-unused-modules/typescript/exports-for-dynamic-ts.ts @@ -0,0 +1,5 @@ +export const ts_a = 10 +export const ts_b = 20 +export const ts_c = 30 +const ts_d = 40 +export default ts_d diff --git a/tests/src/rules/no-unused-modules.js b/tests/src/rules/no-unused-modules.js index 837f7e575..6f87058c4 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -112,41 +112,49 @@ ruleTester.run('no-unused-modules', rule, { options: unusedExportsOptions, code: 'import { o2 } from "./file-o";export default () => 12', filename: testFilePath('./no-unused-modules/file-a.js'), + parser: require.resolve('babel-eslint'), }), test({ options: unusedExportsOptions, code: 'export const b = 2', filename: testFilePath('./no-unused-modules/file-b.js'), + parser: require.resolve('babel-eslint'), }), test({ options: unusedExportsOptions, code: 'const c1 = 3; function c2() { return 3 }; export { c1, c2 }', filename: testFilePath('./no-unused-modules/file-c.js'), + parser: require.resolve('babel-eslint'), }), test({ options: unusedExportsOptions, code: 'export function d() { return 4 }', filename: testFilePath('./no-unused-modules/file-d.js'), + parser: require.resolve('babel-eslint'), }), test({ options: unusedExportsOptions, code: 'export class q { q0() {} }', filename: testFilePath('./no-unused-modules/file-q.js'), + parser: require.resolve('babel-eslint'), }), test({ options: unusedExportsOptions, code: 'const e0 = 5; export { e0 as e }', filename: testFilePath('./no-unused-modules/file-e.js'), + parser: require.resolve('babel-eslint'), }), test({ options: unusedExportsOptions, code: 'const l0 = 5; const l = 10; export { l0 as l1, l }; export default () => {}', filename: testFilePath('./no-unused-modules/file-l.js'), + parser: require.resolve('babel-eslint'), }), test({ options: unusedExportsOptions, code: 'const o0 = 0; const o1 = 1; export { o0, o1 as o2 }; export default () => {}', filename: testFilePath('./no-unused-modules/file-o.js'), + parser: require.resolve('babel-eslint'), }), ], invalid: [ @@ -234,7 +242,72 @@ ruleTester.run('no-unused-modules', rule, { ], }); -// test for export from + +describe('dynamic imports', () => { + if (semver.satisfies(eslintPkg.version, '< 6')) { + beforeEach(function () { + this.skip(); + }); + return; + } + + // test for unused exports with `import()` + ruleTester.run('no-unused-modules', rule, { + valid: [ + test({ + options: unusedExportsOptions, + code: ` + export const a = 10 + export const b = 20 + export const c = 30 + const d = 40 + export default d + `, + parser: require.resolve('babel-eslint'), + filename: testFilePath('./no-unused-modules/exports-for-dynamic-js.js'), + }), + ], + invalid: [ + test({ + options: unusedExportsOptions, + code: ` + export const a = 10 + export const b = 20 + export const c = 30 + const d = 40 + export default d + `, + parser: require.resolve('babel-eslint'), + filename: testFilePath('./no-unused-modules/exports-for-dynamic-js-2.js'), + errors: [ + error(`exported declaration 'a' not used within other modules`), + error(`exported declaration 'b' not used within other modules`), + error(`exported declaration 'c' not used within other modules`), + error(`exported declaration 'default' not used within other modules`), + ] }), + ], + }); + typescriptRuleTester.run('no-unused-modules', rule, { + valid: [ + test({ + options: unusedExportsTypescriptOptions, + code: ` + export const ts_a = 10 + export const ts_b = 20 + export const ts_c = 30 + const ts_d = 40 + export default ts_d + `, + parser: require.resolve('@typescript-eslint/parser'), + filename: testFilePath('./no-unused-modules/typescript/exports-for-dynamic-ts.ts'), + }), + ], + invalid: [ + ], + }); +}); + +// // test for export from ruleTester.run('no-unused-modules', rule, { valid: [ test({ From 1571913aa1dd5e2eba2b3892e16a60d6197f02eb Mon Sep 17 00:00:00 2001 From: Greg Walker Date: Thu, 6 May 2021 14:55:00 -0500 Subject: [PATCH 13/33] [utils] [new] create internal replacement for `pkg-up` and `read-pkg-up` --- utils/CHANGELOG.md | 8 ++++--- utils/package.json | 1 + utils/pkgUp.js | 8 +++++++ utils/readPkgUp.js | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 utils/pkgUp.js create mode 100644 utils/readPkgUp.js diff --git a/utils/CHANGELOG.md b/utils/CHANGELOG.md index 241a205b4..7d08f1963 100644 --- a/utils/CHANGELOG.md +++ b/utils/CHANGELOG.md @@ -8,6 +8,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ### Added - `fileExistsWithCaseSync`: add `strict` argument ([#1262], thanks [@sergei-startsev]) - add `visit`, to support dynamic imports ([#1660], [#2212], thanks [@maxkomarychev], [@aladdin-add], [@Hypnosphi]) +- create internal replacement for `pkg-up` and `read-pkg-up` ([#2047], [@mgwalker]) ## v2.6.2 - 2021-08-08 @@ -96,6 +97,7 @@ Yanked due to critical issue with cache key resulting from #839. [#2212]: https://github.com/import-js/eslint-plugin-import/pull/2212 [#2160]: https://github.com/import-js/eslint-plugin-import/pull/2160 +[#2047]: https://github.com/import-js/eslint-plugin-import/pull/2047 [#2026]: https://github.com/import-js/eslint-plugin-import/pull/2026 [#1786]: https://github.com/import-js/eslint-plugin-import/pull/1786 [#1671]: https://github.com/import-js/eslint-plugin-import/pull/1671 @@ -121,15 +123,15 @@ Yanked due to critical issue with cache key resulting from #839. [@brettz9]: https://github.com/brettz9 [@christophercurrie]: https://github.com/christophercurrie [@hulkish]: https://github.com/hulkish +[@Hypnosphi]: https://github.com/Hypnosphi [@iamnapo]: https://github.com/iamnapo [@JounQin]: https://github.com/JounQin [@kaiyoma]: https://github.com/kaiyoma [@manuth]: https://github.com/manuth +[@maxkomarychev]: https://github.com/maxkomarychev +[@mgwalker]: https://github.com/mgwalker [@pmcelhaney]: https://github.com/pmcelhaney [@sergei-startsev]: https://github.com/sergei-startsev [@sompylasar]: https://github.com/sompylasar [@timkraut]: https://github.com/timkraut [@vikr01]: https://github.com/vikr01 -[@maxkomarychev]: https://github.com/maxkomarychev -[@aladdin-add]: https://github.com/aladdin-add -[@Hypnosphi]: https://github.com/Hypnosphi \ No newline at end of file diff --git a/utils/package.json b/utils/package.json index f726b7958..787ce83a7 100644 --- a/utils/package.json +++ b/utils/package.json @@ -27,6 +27,7 @@ "homepage": "https://github.com/import-js/eslint-plugin-import#readme", "dependencies": { "debug": "^3.2.7", + "find-up": "^2.1.0", "pkg-dir": "^2.0.0" } } diff --git a/utils/pkgUp.js b/utils/pkgUp.js new file mode 100644 index 000000000..f73e3f7b2 --- /dev/null +++ b/utils/pkgUp.js @@ -0,0 +1,8 @@ +'use strict'; +exports.__esModule = true; + +const findUp = require('find-up'); + +exports.default = function pkgUp(opts) { + return findUp.sync('package.json', opts); +}; diff --git a/utils/readPkgUp.js b/utils/readPkgUp.js new file mode 100644 index 000000000..245afde68 --- /dev/null +++ b/utils/readPkgUp.js @@ -0,0 +1,52 @@ +'use strict'; +exports.__esModule = true; + +const fs = require('fs'); +const pkgUp = require('./pkgUp').default; + +function stripBOM(str) { + return str.replace(/^\uFEFF/, ''); +} + +/** + * 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 + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +exports.default = function readPkgUp(opts) { + const fp = pkgUp(opts.cwd); + + if (!fp) { + return {}; + } + + try { + return { + pkg: JSON.parse(stripBOM(fs.readFileSync(fp, { encoding: 'utf-8' }))), + path: fp, + }; + } catch (e) { + return {}; + } +}; From 9ccdcb758f37ae5efe464699a5442d98cf1f73f3 Mon Sep 17 00:00:00 2001 From: Greg Walker Date: Thu, 6 May 2021 14:55:00 -0500 Subject: [PATCH 14/33] [Refactor] switch to an internal replacement for `pkg-up` and `read-pkg-up` --- CHANGELOG.md | 9 +++++++-- package.json | 3 --- src/core/packagePath.js | 8 ++++---- src/rules/no-extraneous-dependencies.js | 4 ++-- src/rules/no-import-module-exports.js | 4 ++-- src/rules/no-relative-packages.js | 4 ++-- src/rules/no-unused-modules.js | 4 ++-- tests/files/package.json | 2 +- utils/readPkgUp.js | 2 +- 9 files changed, 21 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9203739fd..26a6e76fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ## [Unreleased] +### Changed +- [Refactor] switch to an internal replacement for `pkg-up` and `read-pkg-up` ([#2047], [@mgwalker]) + ### Added - [`no-unresolved`]: add `caseSensitiveStrict` option ([#1262], thanks [@sergei-startsev]) - [`no-unused-modules`]: add eslint v8 support ([#2194], thanks [@coderaiser]) @@ -915,7 +918,7 @@ for info on changes for earlier releases. [#2160]: https://github.com/import-js/eslint-plugin-import/pull/2160 [#2158]: https://github.com/import-js/eslint-plugin-import/pull/2158 [#2156]: https://github.com/import-js/eslint-plugin-import/pull/2156 -[#2149]: https://github.com/benmosher/eslint-plugin-import/pull/2149 +[#2149]: https://github.com/import-js/eslint-plugin-import/pull/2149 [#2146]: https://github.com/import-js/eslint-plugin-import/pull/2146 [#2140]: https://github.com/import-js/eslint-plugin-import/pull/2140 [#2138]: https://github.com/import-js/eslint-plugin-import/pull/2138 @@ -928,6 +931,7 @@ for info on changes for earlier releases. [#2083]: https://github.com/import-js/eslint-plugin-import/pull/2083 [#2075]: https://github.com/import-js/eslint-plugin-import/pull/2075 [#2071]: https://github.com/import-js/eslint-plugin-import/pull/2071 +[#2047]: https://github.com/import-js/eslint-plugin-import/pull/2047 [#2034]: https://github.com/import-js/eslint-plugin-import/pull/2034 [#2028]: https://github.com/import-js/eslint-plugin-import/pull/2028 [#2026]: https://github.com/import-js/eslint-plugin-import/pull/2026 @@ -1172,8 +1176,8 @@ for info on changes for earlier releases. [#2161]: https://github.com/import-js/eslint-plugin-import/issues/2161 [#2118]: https://github.com/import-js/eslint-plugin-import/issues/2118 [#2067]: https://github.com/import-js/eslint-plugin-import/issues/2067 -[#2056]: https://github.com/import-js/eslint-plugin-import/issues/2056 [#2063]: https://github.com/import-js/eslint-plugin-import/issues/2063 +[#2056]: https://github.com/import-js/eslint-plugin-import/issues/2056 [#1998]: https://github.com/import-js/eslint-plugin-import/issues/1998 [#1965]: https://github.com/import-js/eslint-plugin-import/issues/1965 [#1924]: https://github.com/import-js/eslint-plugin-import/issues/1924 @@ -1492,6 +1496,7 @@ for info on changes for earlier releases. [@Maxim-Mazurok]: https://github.com/Maxim-Mazurok [@maxkomarychev]: https://github.com/maxkomarychev [@maxmalov]: https://github.com/maxmalov +[@mgwalker]: https://github.com/mgwalker [@MikeyBeLike]: https://github.com/MikeyBeLike [@mplewis]: https://github.com/mplewis [@nickofthyme]: https://github.com/nickofthyme diff --git a/package.json b/package.json index b845b609d..08d5699f6 100644 --- a/package.json +++ b/package.json @@ -104,14 +104,11 @@ "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", "eslint-module-utils": "^2.6.2", - "find-up": "^2.0.0", "has": "^1.0.3", "is-core-module": "^2.6.0", "is-glob": "^4.0.1", "minimatch": "^3.0.4", "object.values": "^1.1.4", - "pkg-up": "^2.0.0", - "read-pkg-up": "^3.0.0", "resolve": "^1.20.0", "tsconfig-paths": "^3.11.0" } diff --git a/src/core/packagePath.js b/src/core/packagePath.js index a8c3c6763..2b5a2d41e 100644 --- a/src/core/packagePath.js +++ b/src/core/packagePath.js @@ -1,6 +1,6 @@ import { dirname } from 'path'; -import findUp from 'find-up'; -import readPkgUp from 'read-pkg-up'; +import pkgUp from 'eslint-module-utils/pkgUp'; +import readPkgUp from 'eslint-module-utils/readPkgUp'; export function getContextPackagePath(context) { @@ -8,12 +8,12 @@ export function getContextPackagePath(context) { } export function getFilePackagePath(filePath) { - const fp = findUp.sync('package.json', { cwd: filePath }); + const fp = pkgUp({ cwd: filePath }); return dirname(fp); } export function getFilePackageName(filePath) { - const { pkg, path } = readPkgUp.sync({ cwd: filePath, normalize: false }); + const { pkg, path } = readPkgUp({ cwd: filePath, normalize: false }); if (pkg) { // recursion in case of intermediate esm package.json without name found return pkg.name || getFilePackageName(dirname(dirname(path))); diff --git a/src/rules/no-extraneous-dependencies.js b/src/rules/no-extraneous-dependencies.js index 8d2e294cc..06a724a09 100644 --- a/src/rules/no-extraneous-dependencies.js +++ b/src/rules/no-extraneous-dependencies.js @@ -1,6 +1,6 @@ import path from 'path'; import fs from 'fs'; -import readPkgUp from 'read-pkg-up'; +import readPkgUp from 'eslint-module-utils/readPkgUp'; import minimatch from 'minimatch'; import resolve from 'eslint-module-utils/resolve'; import moduleVisitor from 'eslint-module-utils/moduleVisitor'; @@ -69,7 +69,7 @@ function getDependencies(context, packageDir) { Object.assign( packageContent, extractDepFields( - readPkgUp.sync({ cwd: context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(), normalize: false }).pkg + readPkgUp({ cwd: context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(), normalize: false }).pkg ) ); } diff --git a/src/rules/no-import-module-exports.js b/src/rules/no-import-module-exports.js index 50ba212c8..45710929c 100644 --- a/src/rules/no-import-module-exports.js +++ b/src/rules/no-import-module-exports.js @@ -1,9 +1,9 @@ import minimatch from 'minimatch'; import path from 'path'; -import pkgUp from 'pkg-up'; +import pkgUp from 'eslint-module-utils/pkgUp'; function getEntryPoint(context) { - const pkgPath = pkgUp.sync(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()); + const pkgPath = pkgUp({ cwd: context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename() }); try { return require.resolve(path.dirname(pkgPath)); } catch (error) { diff --git a/src/rules/no-relative-packages.js b/src/rules/no-relative-packages.js index 90c1ecc70..714eb3f5e 100644 --- a/src/rules/no-relative-packages.js +++ b/src/rules/no-relative-packages.js @@ -1,5 +1,5 @@ import path from 'path'; -import readPkgUp from 'read-pkg-up'; +import readPkgUp from 'eslint-module-utils/readPkgUp'; import resolve from 'eslint-module-utils/resolve'; import moduleVisitor, { makeOptionsSchema } from 'eslint-module-utils/moduleVisitor'; @@ -7,7 +7,7 @@ import importType from '../core/importType'; import docsUrl from '../docsUrl'; function findNamedPackage(filePath) { - const found = readPkgUp.sync({ cwd: filePath, normalize: false }); + const found = readPkgUp({ cwd: filePath }); if (found.pkg && !found.pkg.name) { return findNamedPackage(path.join(found.path, '../..')); } diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index ab347bd8c..068eb911c 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -10,7 +10,7 @@ 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 'read-pkg-up'; +import readPkgUp from 'eslint-module-utils/readPkgUp'; import values from 'object.values'; import includes from 'array-includes'; @@ -352,7 +352,7 @@ const newDefaultImportExists = specifiers => specifiers.some(({ type }) => type === IMPORT_DEFAULT_SPECIFIER); const fileIsInPkg = file => { - const { path, pkg } = readPkgUp.sync({ cwd: file, normalize: false }); + const { path, pkg } = readPkgUp({ cwd: file }); const basePath = dirname(path); const checkPkgFieldString = pkgField => { diff --git a/tests/files/package.json b/tests/files/package.json index de1d80275..365f02b6e 100644 --- a/tests/files/package.json +++ b/tests/files/package.json @@ -12,7 +12,7 @@ "esm-package": "^1.0.0", "jquery": "^3.1.0", "lodash.cond": "^4.3.0", - "pkg-up": "^1.0.0", + "find-up": "^1.0.0", "rxjs": "^1.0.0" }, "optionalDependencies": { diff --git a/utils/readPkgUp.js b/utils/readPkgUp.js index 245afde68..6a6a1eea3 100644 --- a/utils/readPkgUp.js +++ b/utils/readPkgUp.js @@ -35,7 +35,7 @@ function stripBOM(str) { * THE SOFTWARE. */ exports.default = function readPkgUp(opts) { - const fp = pkgUp(opts.cwd); + const fp = pkgUp(opts); if (!fp) { return {}; From 4d15e268b694e9e9946ea16d9e1b94de1b850d7c Mon Sep 17 00:00:00 2001 From: Jack Bates Date: Fri, 3 Sep 2021 15:45:36 -0700 Subject: [PATCH 15/33] [patch] TypeScript config: remove `.d.ts` from `import/parsers` setting and `import/extensions` setting --- CHANGELOG.md | 7 +++++-- config/typescript.js | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 26a6e76fb..c430385c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,8 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ## [Unreleased] ### Changed -- [Refactor] switch to an internal replacement for `pkg-up` and `read-pkg-up` ([#2047], [@mgwalker]) +- [Refactor] switch to an internal replacement for `pkg-up` and `read-pkg-up` ([#2047], thanks [@mgwalker]) +- [patch] TypeScript config: remove `.d.ts` from [`import/parsers` setting] and [`import/extensions` setting] ([#2220], thanks [@jablko]) ### Added - [`no-unresolved`]: add `caseSensitiveStrict` option ([#1262], thanks [@sergei-startsev]) @@ -230,7 +231,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [`order`]: add option pathGroupsExcludedImportTypes to allow ordering of external import types ([#1565], thanks [@Mairu]) ### Fixed -- [`no-unused-modules`]: fix usage of `import/extensions` settings ([#1560], thanks [@stekycz]) +- [`no-unused-modules`]: fix usage of [`import/extensions` setting] ([#1560], thanks [@stekycz]) - [`extensions`]: ignore non-main modules ([#1563], thanks [@saschanaz]) - TypeScript config: lookup for external modules in @types folder ([#1526], thanks [@joaovieira]) - [`no-extraneous-dependencies`]: ensure `node.source` is truthy ([#1589], thanks [@ljharb]) @@ -909,6 +910,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2220]: https://github.com/import-js/eslint-plugin-import/pull/2220 [#2219]: https://github.com/import-js/eslint-plugin-import/pull/2219 [#2212]: https://github.com/import-js/eslint-plugin-import/pull/2212 [#2196]: https://github.com/import-js/eslint-plugin-import/pull/2196 @@ -1444,6 +1446,7 @@ for info on changes for earlier releases. [@isiahmeadows]: https://github.com/isiahmeadows [@IvanGoncharov]: https://github.com/IvanGoncharov [@ivo-stefchev]: https://github.com/ivo-stefchev +[@jablko]: https://github.com/jablko [@jakubsta]: https://github.com/jakubsta [@jeffshaver]: https://github.com/jeffshaver [@jf248]: https://github.com/jf248 diff --git a/config/typescript.js b/config/typescript.js index 01b59f06b..ed03fb3f6 100644 --- a/config/typescript.js +++ b/config/typescript.js @@ -2,7 +2,10 @@ * Adds `.jsx`, `.ts` and `.tsx` as an extension, and enables JSX/TSX parsing. */ -const allExtensions = ['.ts', '.tsx', '.d.ts', '.js', '.jsx']; +// Omit `.d.ts` because 1) TypeScript compilation already confirms that +// types are resolved, and 2) it would mask an unresolved +// `.ts`/`.tsx`/`.js`/`.jsx` implementation. +const allExtensions = ['.ts', '.tsx', '.js', '.jsx']; module.exports = { @@ -10,7 +13,7 @@ module.exports = { 'import/extensions': allExtensions, 'import/external-module-folders': ['node_modules', 'node_modules/@types'], 'import/parsers': { - '@typescript-eslint/parser': ['.ts', '.tsx', '.d.ts'], + '@typescript-eslint/parser': ['.ts', '.tsx'], }, 'import/resolver': { 'node': { From 4ed78671abf9768af2aec4ca61c377fed2e93f5f Mon Sep 17 00:00:00 2001 From: Jack Bates Date: Sat, 11 Sep 2021 07:36:41 -0700 Subject: [PATCH 16/33] [Fix] `no-unresolved`: ignore type-only imports --- CHANGELOG.md | 11 +++++++---- src/rules/no-unresolved.js | 5 +++++ tests/src/rules/no-unresolved.js | 22 +++++++++++++++++++++- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c430385c3..dbb9ecb82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,16 +6,19 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ## [Unreleased] -### Changed -- [Refactor] switch to an internal replacement for `pkg-up` and `read-pkg-up` ([#2047], thanks [@mgwalker]) -- [patch] TypeScript config: remove `.d.ts` from [`import/parsers` setting] and [`import/extensions` setting] ([#2220], thanks [@jablko]) - ### Added - [`no-unresolved`]: add `caseSensitiveStrict` option ([#1262], thanks [@sergei-startsev]) - [`no-unused-modules`]: add eslint v8 support ([#2194], thanks [@coderaiser]) - [`no-restricted-paths`]: add/restore glob pattern support ([#2219], thanks [@stropho]) - [`no-unused-modules`]: support dynamic imports ([#1660], [#2212], thanks [@maxkomarychev], [@aladdin-add], [@Hypnosphi]) +### Fixed +- [`no-unresolved`]: ignore type-only imports ([#2220], thanks [@jablko]) + +### Changed +- [Refactor] switch to an internal replacement for `pkg-up` and `read-pkg-up` ([#2047], thanks [@mgwalker]) +- [patch] TypeScript config: remove `.d.ts` from [`import/parsers` setting] and [`import/extensions` setting] ([#2220], thanks [@jablko]) + ## [2.24.2] - 2021-08-24 ### Fixed diff --git a/src/rules/no-unresolved.js b/src/rules/no-unresolved.js index d7212560c..408b6ff5e 100644 --- a/src/rules/no-unresolved.js +++ b/src/rules/no-unresolved.js @@ -27,6 +27,11 @@ module.exports = { const options = context.options[0] || {}; function checkSourceValue(source) { + // ignore type-only imports + if (source.parent && source.parent.importKind === 'type') { + return; + } + const caseSensitive = !CASE_SENSITIVE_FS && options.caseSensitive !== false; const caseSensitiveStrict = !CASE_SENSITIVE_FS && options.caseSensitiveStrict; diff --git a/tests/src/rules/no-unresolved.js b/tests/src/rules/no-unresolved.js index 19203074e..9e6db8c04 100644 --- a/tests/src/rules/no-unresolved.js +++ b/tests/src/rules/no-unresolved.js @@ -1,6 +1,6 @@ import * as path from 'path'; -import { test, SYNTAX_CASES, testVersion } from '../utils'; +import { getTSParsers, test, SYNTAX_CASES, testVersion } from '../utils'; import { CASE_SENSITIVE_FS } from 'eslint-module-utils/resolve'; @@ -441,3 +441,23 @@ ruleTester.run('import() with built-in parser', rule, { })) || [], ), }); + +context('TypeScript', () => { + getTSParsers().filter(x => x !== require.resolve('typescript-eslint-parser')).forEach((parser) => { + ruleTester.run(`${parser}: no-unresolved ignore type-only`, rule, { + valid: [ + test({ + code: 'import type { JSONSchema7Type } from "@types/json-schema";', + parser, + }), + ], + invalid: [ + test({ + code: 'import { JSONSchema7Type } from "@types/json-schema";', + errors: [ "Unable to resolve path to module '@types/json-schema'." ], + parser, + }), + ], + }); + }); +}); From 47ea669d2c68b2e6e67ed20d93b71d42147dbccd Mon Sep 17 00:00:00 2001 From: Remco Haszing Date: Sun, 12 Sep 2021 16:08:21 +0200 Subject: [PATCH 17/33] [Fix] `order`: Fix import ordering in TypeScript module declarations Without this, `import/order` checks if all imports in a file are sorted. The autofix would then move all imports to the type of the file, breaking TypeScript module declarations. Closes #2217 --- CHANGELOG.md | 3 +++ src/rules/order.js | 47 ++++++++++++++++++++++++------------- tests/src/rules/order.js | 50 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbb9ecb82..803333d08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ### Fixed - [`no-unresolved`]: ignore type-only imports ([#2220], thanks [@jablko]) +- [`order`]: fix sorting imports inside TypeScript module declarations ([#2226], thanks [@remcohaszing]) ### Changed - [Refactor] switch to an internal replacement for `pkg-up` and `read-pkg-up` ([#2047], thanks [@mgwalker]) @@ -913,6 +914,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2226]: https://github.com/import-js/eslint-plugin-import/pull/2226 [#2220]: https://github.com/import-js/eslint-plugin-import/pull/2220 [#2219]: https://github.com/import-js/eslint-plugin-import/pull/2219 [#2212]: https://github.com/import-js/eslint-plugin-import/pull/2212 @@ -1520,6 +1522,7 @@ for info on changes for earlier releases. [@ramasilveyra]: https://github.com/ramasilveyra [@randallreedjr]: https://github.com/randallreedjr [@redbugz]: https://github.com/redbugz +[@remcohaszing]: https://github.com/remcohaszing [@rfermann]: https://github.com/rfermann [@rhettlivingston]: https://github.com/rhettlivingston [@rhys-vdw]: https://github.com/rhys-vdw diff --git a/src/rules/order.js b/src/rules/order.js index 2f4ef08b7..194a3fd53 100644 --- a/src/rules/order.js +++ b/src/rules/order.js @@ -336,7 +336,7 @@ function registerNode(context, importEntry, ranks, imported, excludedImportTypes } } -function isModuleLevelRequire(node) { +function getRequireBlock(node) { let n = node; // Handle cases like `const baz = require('foo').bar.baz` // and `const foo = require('foo')()` @@ -346,11 +346,13 @@ function isModuleLevelRequire(node) { ) { n = n.parent; } - return ( + if ( n.parent.type === 'VariableDeclarator' && n.parent.parent.type === 'VariableDeclaration' && n.parent.parent.parent.type === 'Program' - ); + ) { + return n.parent.parent.parent; + } } const types = ['builtin', 'external', 'internal', 'unknown', 'parent', 'sibling', 'index', 'object', 'type']; @@ -605,7 +607,14 @@ module.exports = { }, }; } - let imported = []; + const importMap = new Map(); + + function getBlockImports(node) { + if (!importMap.has(node)) { + importMap.set(node, []); + } + return importMap.get(node); + } return { ImportDeclaration: function handleImports(node) { @@ -621,7 +630,7 @@ module.exports = { type: 'import', }, ranks, - imported, + getBlockImports(node.parent), pathGroupsExcludedImportTypes ); } @@ -652,12 +661,16 @@ module.exports = { type, }, ranks, - imported, + getBlockImports(node.parent), pathGroupsExcludedImportTypes ); }, CallExpression: function handleRequires(node) { - if (!isStaticRequire(node) || !isModuleLevelRequire(node)) { + if (!isStaticRequire(node)) { + return; + } + const block = getRequireBlock(node); + if (!block) { return; } const name = node.arguments[0].value; @@ -670,22 +683,24 @@ module.exports = { type: 'require', }, ranks, - imported, + getBlockImports(block), pathGroupsExcludedImportTypes ); }, 'Program:exit': function reportAndReset() { - if (newlinesBetweenImports !== 'ignore') { - makeNewlinesBetweenReport(context, imported, newlinesBetweenImports); - } + importMap.forEach((imported) => { + if (newlinesBetweenImports !== 'ignore') { + makeNewlinesBetweenReport(context, imported, newlinesBetweenImports); + } - if (alphabetize.order !== 'ignore') { - mutateRanksToAlphabetize(imported, alphabetize); - } + if (alphabetize.order !== 'ignore') { + mutateRanksToAlphabetize(imported, alphabetize); + } - makeOutOfOrderReport(context, imported); + makeOutOfOrderReport(context, imported); + }); - imported = []; + importMap.clear(); }, }; }, diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index 61bf1bb70..146306259 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -2462,6 +2462,24 @@ context('TypeScript', function () { }, ], }), + // Imports inside module declaration + test({ + code: ` + import type { CopyOptions } from 'fs'; + import type { ParsedPath } from 'path'; + + declare module 'my-module' { + import type { CopyOptions } from 'fs'; + import type { ParsedPath } from 'path'; + } + `, + ...parserConfig, + options: [ + { + alphabetize: { order: 'asc' }, + }, + ], + }), ], invalid: [ // Option alphabetize: {order: 'asc'} @@ -2655,6 +2673,38 @@ context('TypeScript', function () { }], options: [{ warnOnUnassignedImports: true }], }), + // Imports inside module declaration + test({ + code: ` + import type { ParsedPath } from 'path'; + import type { CopyOptions } from 'fs'; + + declare module 'my-module' { + import type { ParsedPath } from 'path'; + import type { CopyOptions } from 'fs'; + } + `, + output: ` + import type { CopyOptions } from 'fs'; + import type { ParsedPath } from 'path'; + + declare module 'my-module' { + import type { CopyOptions } from 'fs'; + import type { ParsedPath } from 'path'; + } + `, + errors: [{ + message: '`fs` import should occur before import of `path`', + },{ + message: '`fs` import should occur before import of `path`', + }], + ...parserConfig, + options: [ + { + alphabetize: { order: 'asc' }, + }, + ], + }), ], }); }); From 58fe766e1295ce0532cb44ddd81b661625a0941d Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 18 Sep 2021 09:11:57 -0700 Subject: [PATCH 18/33] [Tests] ignore resolver tests, scripts, and unused memo-parser --- .nycrc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.nycrc b/.nycrc index 20bcfca22..a19f5b8b1 100644 --- a/.nycrc +++ b/.nycrc @@ -10,6 +10,9 @@ "exclude": [ "coverage", "test", - "tests" + "tests", + "resolvers/*/test", + "scripts", + "memo-parser" ] } From 64423e98dee4ea7185e16796a981ec53537f2913 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 18 Sep 2021 17:01:24 -0700 Subject: [PATCH 19/33] [Tests] add passing test for export-star Closes #2093 --- tests/files/export-star-4/module/feature.jsx | 3 +++ tests/files/export-star-4/module/index.ts | 1 + tests/src/rules/export.js | 12 ++++++++++++ 3 files changed, 16 insertions(+) create mode 100644 tests/files/export-star-4/module/feature.jsx create mode 100644 tests/files/export-star-4/module/index.ts diff --git a/tests/files/export-star-4/module/feature.jsx b/tests/files/export-star-4/module/feature.jsx new file mode 100644 index 000000000..82fab1e50 --- /dev/null +++ b/tests/files/export-star-4/module/feature.jsx @@ -0,0 +1,3 @@ +export function func() { + console.log('Hello world'); +} diff --git a/tests/files/export-star-4/module/index.ts b/tests/files/export-star-4/module/index.ts new file mode 100644 index 000000000..42a9f556a --- /dev/null +++ b/tests/files/export-star-4/module/index.ts @@ -0,0 +1 @@ +export * from './feature'; diff --git a/tests/src/rules/export.js b/tests/src/rules/export.js index efc6e402f..c4e3e8e60 100644 --- a/tests/src/rules/export.js +++ b/tests/src/rules/export.js @@ -245,6 +245,18 @@ context('TypeScript', function () { export {Bar as default}; `, }, parserConfig)), + + test({ + ...parserConfig, + code: ` + export * from './module'; + `, + filename: testFilePath('export-star-4/index.js'), + settings: { + ...parserConfig.settings, + 'import/extensions': ['.js', '.ts', '.jsx'], + }, + }), ], invalid: [ // type/value name clash From fd85369fc219c40862d2afcfb11ad053387ba967 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sat, 18 Sep 2021 22:50:22 -0700 Subject: [PATCH 20/33] [Tests] skip failing test on eslint < 6 + node < 8 --- tests/src/rules/export.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/rules/export.js b/tests/src/rules/export.js index c4e3e8e60..29fae4101 100644 --- a/tests/src/rules/export.js +++ b/tests/src/rules/export.js @@ -246,7 +246,7 @@ context('TypeScript', function () { `, }, parserConfig)), - test({ + ...(semver.satisfies(process.version, '< 8') && semver.satisfies(eslintPkg.version, '< 6') ? [] : test({ ...parserConfig, code: ` export * from './module'; @@ -256,7 +256,7 @@ context('TypeScript', function () { ...parserConfig.settings, 'import/extensions': ['.js', '.ts', '.jsx'], }, - }), + })), ], invalid: [ // type/value name clash From 471790f9ff05c65279d2da8a280c6c57d8b14f65 Mon Sep 17 00:00:00 2001 From: Jack Bates Date: Sun, 19 Sep 2021 16:23:41 -0700 Subject: [PATCH 21/33] [Tests] fix skip usage --- tests/src/rules/max-dependencies.js | 2 +- tests/src/rules/no-extraneous-dependencies.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/src/rules/max-dependencies.js b/tests/src/rules/max-dependencies.js index fc3b541f5..bab38496e 100644 --- a/tests/src/rules/max-dependencies.js +++ b/tests/src/rules/max-dependencies.js @@ -91,7 +91,7 @@ ruleTester.run('max-dependencies', rule, { ], }); -context('TypeScript', { skip: semver.satisfies(eslintPkg.version, '>5.0.0') }, () => { +(semver.satisfies(eslintPkg.version, '>5.0.0') ? describe.skip : describe)('TypeScript', () => { getTSParsers().forEach((parser) => { ruleTester.run(`max-dependencies (${parser.replace(process.cwd(), '.')})`, rule, { valid: [ diff --git a/tests/src/rules/no-extraneous-dependencies.js b/tests/src/rules/no-extraneous-dependencies.js index c6abcd6e4..af139d7b6 100644 --- a/tests/src/rules/no-extraneous-dependencies.js +++ b/tests/src/rules/no-extraneous-dependencies.js @@ -381,7 +381,7 @@ ruleTester.run('no-extraneous-dependencies', rule, { }); // TODO: figure out why these tests fail in eslint 4 -describe('TypeScript', { skip: semver.satisfies(eslintPkg.version, '^4') }, function () { +(semver.satisfies(eslintPkg.version, '^4') ? describe.skip : describe)('TypeScript', () => { getTSParsers().forEach((parser) => { const parserConfig = { parser, From 28669b97cea32e01f873fc62a524e48e4d99feb0 Mon Sep 17 00:00:00 2001 From: Jack Bates Date: Sun, 19 Sep 2021 16:57:29 -0700 Subject: [PATCH 22/33] [Tests] `no-extraneous-dependencies` ignores unresolved imports --- .../with-typescript-dev-dependencies/package.json | 2 +- tests/src/rules/no-extraneous-dependencies.js | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/files/with-typescript-dev-dependencies/package.json b/tests/files/with-typescript-dev-dependencies/package.json index e17fbd977..f859f5085 100644 --- a/tests/files/with-typescript-dev-dependencies/package.json +++ b/tests/files/with-typescript-dev-dependencies/package.json @@ -1,5 +1,5 @@ { "devDependencies": { - "@types/json-schema": "*" + "a": "*" } } diff --git a/tests/src/rules/no-extraneous-dependencies.js b/tests/src/rules/no-extraneous-dependencies.js index af139d7b6..29604af17 100644 --- a/tests/src/rules/no-extraneous-dependencies.js +++ b/tests/src/rules/no-extraneous-dependencies.js @@ -395,16 +395,16 @@ ruleTester.run('no-extraneous-dependencies', rule, { ruleTester.run('no-extraneous-dependencies', rule, { valid: [ test(Object.assign({ - code: 'import type { JSONSchema7Type } from "@types/json-schema";', + code: 'import type T from "a";', options: [{ packageDir: packageDirWithTypescriptDevDependencies, devDependencies: false }], }, parserConfig)), ], invalid: [ test(Object.assign({ - code: 'import { JSONSchema7Type } from "@types/json-schema";', + code: 'import T from "a";', options: [{ packageDir: packageDirWithTypescriptDevDependencies, devDependencies: false }], errors: [{ - message: "'@types/json-schema' should be listed in the project's dependencies, not devDependencies.", + message: "'a' should be listed in the project's dependencies, not devDependencies.", }], }, parserConfig)), ], @@ -414,17 +414,17 @@ ruleTester.run('no-extraneous-dependencies', rule, { valid: [], invalid: [ test(Object.assign({ - code: 'import { JSONSchema7Type } from "@types/json-schema"; /* typescript-eslint-parser */', + code: 'import T from "a"; /* typescript-eslint-parser */', options: [{ packageDir: packageDirWithTypescriptDevDependencies, devDependencies: false }], errors: [{ - message: "'@types/json-schema' should be listed in the project's dependencies, not devDependencies.", + message: "'a' should be listed in the project's dependencies, not devDependencies.", }], }, parserConfig)), test(Object.assign({ - code: 'import type { JSONSchema7Type } from "@types/json-schema"; /* typescript-eslint-parser */', + code: 'import type T from "a"; /* typescript-eslint-parser */', options: [{ packageDir: packageDirWithTypescriptDevDependencies, devDependencies: false }], errors: [{ - message: "'@types/json-schema' should be listed in the project's dependencies, not devDependencies.", + message: "'a' should be listed in the project's dependencies, not devDependencies.", }], }, parserConfig)), ], From 47e9c89aa7e4e565b166d1b00f01484c3c911582 Mon Sep 17 00:00:00 2001 From: Jack Bates Date: Mon, 20 Sep 2021 11:52:43 -0700 Subject: [PATCH 23/33] [Tests] type-only imports were added in TypeScript ESTree 2.23.0 --- tests/src/rules/first.js | 1 - tests/src/rules/max-dependencies.js | 81 +++++++++--------- tests/src/rules/no-duplicates.js | 1 + tests/src/rules/no-extraneous-dependencies.js | 82 +++++++------------ tests/src/rules/no-unresolved.js | 1 + tests/src/rules/order.js | 1 + 6 files changed, 73 insertions(+), 94 deletions(-) diff --git a/tests/src/rules/first.js b/tests/src/rules/first.js index 9d2787067..05328e51e 100644 --- a/tests/src/rules/first.js +++ b/tests/src/rules/first.js @@ -80,7 +80,6 @@ ruleTester.run('first', rule, { context('TypeScript', function () { getTSParsers() - .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) .forEach((parser) => { const parserConfig = { parser, diff --git a/tests/src/rules/max-dependencies.js b/tests/src/rules/max-dependencies.js index bab38496e..6d80bbf04 100644 --- a/tests/src/rules/max-dependencies.js +++ b/tests/src/rules/max-dependencies.js @@ -1,8 +1,6 @@ import { test, getTSParsers } from '../utils'; import { RuleTester } from 'eslint'; -import eslintPkg from 'eslint/package.json'; -import semver from 'semver'; const ruleTester = new RuleTester(); const rule = require('rules/max-dependencies'); @@ -91,43 +89,46 @@ ruleTester.run('max-dependencies', rule, { ], }); -(semver.satisfies(eslintPkg.version, '>5.0.0') ? describe.skip : describe)('TypeScript', () => { - getTSParsers().forEach((parser) => { - ruleTester.run(`max-dependencies (${parser.replace(process.cwd(), '.')})`, rule, { - valid: [ - test({ - code: 'import type { x } from \'./foo\'; import { y } from \'./bar\';', - parser, - options: [{ - max: 1, - ignoreTypeImports: true, - }], - }), - ], - invalid: [ - test({ - code: 'import type { x } from \'./foo\'; import type { y } from \'./bar\'', - parser, - options: [{ - max: 1, - }], - errors: [ - 'Maximum number of dependencies (1) exceeded.', - ], - }), - - test({ - code: 'import type { x } from \'./foo\'; import type { y } from \'./bar\'; import type { z } from \'./baz\'', - parser, - options: [{ - max: 2, - ignoreTypeImports: false, - }], - errors: [ - 'Maximum number of dependencies (2) exceeded.', - ], - }), - ], +describe('TypeScript', () => { + getTSParsers() + // Type-only imports were added in TypeScript ESTree 2.23.0 + .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) + .forEach((parser) => { + ruleTester.run(`max-dependencies (${parser.replace(process.cwd(), '.')})`, rule, { + valid: [ + test({ + code: 'import type { x } from \'./foo\'; import { y } from \'./bar\';', + parser, + options: [{ + max: 1, + ignoreTypeImports: true, + }], + }), + ], + invalid: [ + test({ + code: 'import type { x } from \'./foo\'; import type { y } from \'./bar\'', + parser, + options: [{ + max: 1, + }], + errors: [ + 'Maximum number of dependencies (1) exceeded.', + ], + }), + + test({ + code: 'import type { x } from \'./foo\'; import type { y } from \'./bar\'; import type { z } from \'./baz\'', + parser, + options: [{ + max: 2, + ignoreTypeImports: false, + }], + errors: [ + 'Maximum number of dependencies (2) exceeded.', + ], + }), + ], + }); }); - }); }); diff --git a/tests/src/rules/no-duplicates.js b/tests/src/rules/no-duplicates.js index e550b63ce..ad39543f8 100644 --- a/tests/src/rules/no-duplicates.js +++ b/tests/src/rules/no-duplicates.js @@ -417,6 +417,7 @@ import {x,y} from './foo' context('TypeScript', function () { getNonDefaultParsers() + // Type-only imports were added in TypeScript ESTree 2.23.0 .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) .forEach((parser) => { const parserConfig = { diff --git a/tests/src/rules/no-extraneous-dependencies.js b/tests/src/rules/no-extraneous-dependencies.js index 29604af17..852dbdf4e 100644 --- a/tests/src/rules/no-extraneous-dependencies.js +++ b/tests/src/rules/no-extraneous-dependencies.js @@ -2,8 +2,6 @@ import { getTSParsers, test, testFilePath } from '../utils'; import typescriptConfig from '../../../config/typescript'; import path from 'path'; import fs from 'fs'; -import semver from 'semver'; -import eslintPkg from 'eslint/package.json'; import { RuleTester } from 'eslint'; import flatMap from 'array.prototype.flatmap'; @@ -380,18 +378,19 @@ ruleTester.run('no-extraneous-dependencies', rule, { ], }); -// TODO: figure out why these tests fail in eslint 4 -(semver.satisfies(eslintPkg.version, '^4') ? describe.skip : describe)('TypeScript', () => { - getTSParsers().forEach((parser) => { - const parserConfig = { - parser, - settings: { - 'import/parsers': { [parser]: ['.ts'] }, - 'import/resolver': { 'eslint-import-resolver-typescript': true }, - }, - }; +describe('TypeScript', () => { + getTSParsers() + // Type-only imports were added in TypeScript ESTree 2.23.0 + .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) + .forEach((parser) => { + const parserConfig = { + parser, + settings: { + 'import/parsers': { [parser]: ['.ts'] }, + 'import/resolver': { 'eslint-import-resolver-typescript': true }, + }, + }; - if (parser !== require.resolve('typescript-eslint-parser')) { ruleTester.run('no-extraneous-dependencies', rule, { valid: [ test(Object.assign({ @@ -409,45 +408,22 @@ ruleTester.run('no-extraneous-dependencies', rule, { }, parserConfig)), ], }); - } else { - ruleTester.run('no-extraneous-dependencies', rule, { - valid: [], - invalid: [ - test(Object.assign({ - code: 'import T from "a"; /* typescript-eslint-parser */', - options: [{ packageDir: packageDirWithTypescriptDevDependencies, devDependencies: false }], - errors: [{ - message: "'a' should be listed in the project's dependencies, not devDependencies.", - }], - }, parserConfig)), - test(Object.assign({ - code: 'import type T from "a"; /* typescript-eslint-parser */', - options: [{ packageDir: packageDirWithTypescriptDevDependencies, devDependencies: false }], - errors: [{ - message: "'a' should be listed in the project's dependencies, not devDependencies.", - }], - }, parserConfig)), - ], - }); - } - }); + }); }); -if (semver.satisfies(eslintPkg.version, '>5.0.0')) { - typescriptRuleTester.run('no-extraneous-dependencies typescript type imports', rule, { - valid: [ - test({ - code: 'import type MyType from "not-a-dependency";', - filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), - parser: require.resolve('babel-eslint'), - }), - test({ - code: 'import type { MyType } from "not-a-dependency";', - filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), - parser: require.resolve('babel-eslint'), - }), - ], - invalid: [ - ], - }); -} +typescriptRuleTester.run('no-extraneous-dependencies typescript type imports', rule, { + valid: [ + test({ + code: 'import type MyType from "not-a-dependency";', + filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), + parser: require.resolve('babel-eslint'), + }), + test({ + code: 'import type { MyType } from "not-a-dependency";', + filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), + parser: require.resolve('babel-eslint'), + }), + ], + invalid: [ + ], +}); diff --git a/tests/src/rules/no-unresolved.js b/tests/src/rules/no-unresolved.js index 9e6db8c04..a5ff6da5d 100644 --- a/tests/src/rules/no-unresolved.js +++ b/tests/src/rules/no-unresolved.js @@ -443,6 +443,7 @@ ruleTester.run('import() with built-in parser', rule, { }); context('TypeScript', () => { + // Type-only imports were added in TypeScript ESTree 2.23.0 getTSParsers().filter(x => x !== require.resolve('typescript-eslint-parser')).forEach((parser) => { ruleTester.run(`${parser}: no-unresolved ignore type-only`, rule, { valid: [ diff --git a/tests/src/rules/order.js b/tests/src/rules/order.js index 146306259..4b15cec89 100644 --- a/tests/src/rules/order.js +++ b/tests/src/rules/order.js @@ -2283,6 +2283,7 @@ ruleTester.run('order', rule, { context('TypeScript', function () { getNonDefaultParsers() + // Type-only imports were added in TypeScript ESTree 2.23.0 .filter((parser) => parser !== require.resolve('typescript-eslint-parser')) .forEach((parser) => { const parserConfig = { From 430d16c9b9937c8695781f871ff87c3d63cda2d3 Mon Sep 17 00:00:00 2001 From: Jack Bates Date: Mon, 20 Sep 2021 17:38:42 -0700 Subject: [PATCH 24/33] [Tests] eslint-import-resolver-typescript@1.0.2 doesn't resolve .js --- tests/src/rules/no-extraneous-dependencies.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/rules/no-extraneous-dependencies.js b/tests/src/rules/no-extraneous-dependencies.js index 852dbdf4e..131604ad9 100644 --- a/tests/src/rules/no-extraneous-dependencies.js +++ b/tests/src/rules/no-extraneous-dependencies.js @@ -387,7 +387,7 @@ describe('TypeScript', () => { parser, settings: { 'import/parsers': { [parser]: ['.ts'] }, - 'import/resolver': { 'eslint-import-resolver-typescript': true }, + 'import/resolver': ['node', 'typescript'], }, }; From 4f0f560544b4d6dcd2d1b7d5880bd5238099f979 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 21 Sep 2021 08:59:37 -0700 Subject: [PATCH 25/33] [Docs] `no-namespace`: fix a typo See https://github.com/import-js/eslint-plugin-import/pull/2112#issuecomment-923994768 --- docs/rules/no-namespace.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/no-namespace.md b/docs/rules/no-namespace.md index e98726051..854f65d6f 100644 --- a/docs/rules/no-namespace.md +++ b/docs/rules/no-namespace.md @@ -22,7 +22,7 @@ import defaultExport, { a, b } from './foobar' ``` ```js -/* eslint import/no-namespace: ["error", {ignore: ['*.ext']] */ +/* eslint import/no-namespace: ["error", {ignore: ['*.ext']}] */ import * as bar from './ignored-module.ext'; ``` From dd814245b7f769a84235f2ffb7204287d47001ba Mon Sep 17 00:00:00 2001 From: Jack Bates Date: Tue, 21 Sep 2021 08:40:32 -0700 Subject: [PATCH 26/33] [Refactor] `no-unresolved`, `no-extraneous-dependencies`: moduleVisitor usage --- CHANGELOG.md | 2 ++ src/rules/no-extraneous-dependencies.js | 1 - src/rules/no-unresolved.js | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 803333d08..6b0f95e28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ### Changed - [Refactor] switch to an internal replacement for `pkg-up` and `read-pkg-up` ([#2047], thanks [@mgwalker]) - [patch] TypeScript config: remove `.d.ts` from [`import/parsers` setting] and [`import/extensions` setting] ([#2220], thanks [@jablko]) +- [Refactor] [`no-unresolved`], [`no-extraneous-dependencies`]: moduleVisitor usage ([#2233], thanks [@jablko]) ## [2.24.2] - 2021-08-24 @@ -914,6 +915,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2233]: https://github.com/import-js/eslint-plugin-import/pull/2233 [#2226]: https://github.com/import-js/eslint-plugin-import/pull/2226 [#2220]: https://github.com/import-js/eslint-plugin-import/pull/2220 [#2219]: https://github.com/import-js/eslint-plugin-import/pull/2219 diff --git a/src/rules/no-extraneous-dependencies.js b/src/rules/no-extraneous-dependencies.js index 06a724a09..25a91aef5 100644 --- a/src/rules/no-extraneous-dependencies.js +++ b/src/rules/no-extraneous-dependencies.js @@ -162,7 +162,6 @@ function reportIfMissing(context, deps, depsOptions, node, name) { // Do not report when importing types if ( node.importKind === 'type' || - (node.parent && node.parent.importKind === 'type') || node.importKind === 'typeof' ) { return; diff --git a/src/rules/no-unresolved.js b/src/rules/no-unresolved.js index 408b6ff5e..549101897 100644 --- a/src/rules/no-unresolved.js +++ b/src/rules/no-unresolved.js @@ -26,9 +26,9 @@ module.exports = { create(context) { const options = context.options[0] || {}; - function checkSourceValue(source) { + function checkSourceValue(source, node) { // ignore type-only imports - if (source.parent && source.parent.importKind === 'type') { + if (node.importKind === 'type') { return; } From 9a744f7385dc12cb3d0376ab7268fed3940e46a4 Mon Sep 17 00:00:00 2001 From: Brody McKee Date: Tue, 28 Sep 2021 12:40:32 +0300 Subject: [PATCH 27/33] [Fix] `default`, `ExportMap`: Resolve extended TypeScript configuration files Fixes #1908. --- CHANGELOG.md | 3 +++ src/ExportMap.js | 22 +++++++++++-------- .../typescript-extended-config/index.d.ts | 3 +++ .../tsconfig.base.json | 5 +++++ .../typescript-extended-config/tsconfig.json | 4 ++++ tests/src/rules/default.js | 11 ++++++++++ 6 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 tests/files/typescript-extended-config/index.d.ts create mode 100644 tests/files/typescript-extended-config/tsconfig.base.json create mode 100644 tests/files/typescript-extended-config/tsconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b0f95e28..264b20c73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ### Fixed - [`no-unresolved`]: ignore type-only imports ([#2220], thanks [@jablko]) - [`order`]: fix sorting imports inside TypeScript module declarations ([#2226], thanks [@remcohaszing]) +- [`default`], `ExportMap`: Resolve extended TypeScript configuration files ([#2240], thanks [@mrmckeb]) ### Changed - [Refactor] switch to an internal replacement for `pkg-up` and `read-pkg-up` ([#2047], thanks [@mgwalker]) @@ -915,6 +916,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2240]: https://github.com/import-js/eslint-plugin-import/pull/2240 [#2233]: https://github.com/import-js/eslint-plugin-import/pull/2233 [#2226]: https://github.com/import-js/eslint-plugin-import/pull/2226 [#2220]: https://github.com/import-js/eslint-plugin-import/pull/2220 @@ -1509,6 +1511,7 @@ for info on changes for earlier releases. [@mgwalker]: https://github.com/mgwalker [@MikeyBeLike]: https://github.com/MikeyBeLike [@mplewis]: https://github.com/mplewis +[@mrmckeb]: https://github.com/mrmckeb [@nickofthyme]: https://github.com/nickofthyme [@nicolashenry]: https://github.com/nicolashenry [@noelebrun]: https://github.com/noelebrun diff --git a/src/ExportMap.js b/src/ExportMap.js index 53091e466..d818fa6ca 100644 --- a/src/ExportMap.js +++ b/src/ExportMap.js @@ -1,4 +1,5 @@ import fs from 'fs'; +import { dirname } from 'path'; import doctrine from 'doctrine'; @@ -18,7 +19,7 @@ import { tsConfigLoader } from 'tsconfig-paths/lib/tsconfig-loader'; import includes from 'array-includes'; -let parseConfigFileTextToJson; +let ts; const log = debug('eslint-plugin-import:ExportMap'); @@ -525,12 +526,15 @@ ExportMap.parse = function (path, content, context) { }); try { if (tsConfigInfo.tsConfigPath !== undefined) { - const jsonText = fs.readFileSync(tsConfigInfo.tsConfigPath).toString(); - if (!parseConfigFileTextToJson) { - // this is because projects not using TypeScript won't have typescript installed - ({ parseConfigFileTextToJson } = require('typescript')); - } - return parseConfigFileTextToJson(tsConfigInfo.tsConfigPath, jsonText).config; + // Projects not using TypeScript won't have `typescript` installed. + if (!ts) { ts = require('typescript'); } + + const configFile = ts.readConfigFile(tsConfigInfo.tsConfigPath, ts.sys.readFile); + return ts.parseJsonConfigFileContent( + configFile.config, + ts.sys, + dirname(tsConfigInfo.tsConfigPath), + ); } } catch (e) { // Catch any errors @@ -545,11 +549,11 @@ ExportMap.parse = function (path, content, context) { }).digest('hex'); let tsConfig = tsConfigCache.get(cacheKey); if (typeof tsConfig === 'undefined') { - tsConfig = readTsConfig(); + tsConfig = readTsConfig(context); tsConfigCache.set(cacheKey, tsConfig); } - return tsConfig && tsConfig.compilerOptions ? tsConfig.compilerOptions.esModuleInterop : false; + return tsConfig && tsConfig.options ? tsConfig.options.esModuleInterop : false; } ast.body.forEach(function (n) { diff --git a/tests/files/typescript-extended-config/index.d.ts b/tests/files/typescript-extended-config/index.d.ts new file mode 100644 index 000000000..2ad4822f7 --- /dev/null +++ b/tests/files/typescript-extended-config/index.d.ts @@ -0,0 +1,3 @@ +export = FooBar; + +declare namespace FooBar {} diff --git a/tests/files/typescript-extended-config/tsconfig.base.json b/tests/files/typescript-extended-config/tsconfig.base.json new file mode 100644 index 000000000..2f9804271 --- /dev/null +++ b/tests/files/typescript-extended-config/tsconfig.base.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "esModuleInterop": true + } +} diff --git a/tests/files/typescript-extended-config/tsconfig.json b/tests/files/typescript-extended-config/tsconfig.json new file mode 100644 index 000000000..97a330960 --- /dev/null +++ b/tests/files/typescript-extended-config/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": {} +} diff --git a/tests/src/rules/default.js b/tests/src/rules/default.js index 15101fc0c..0274e4374 100644 --- a/tests/src/rules/default.js +++ b/tests/src/rules/default.js @@ -231,6 +231,17 @@ context('TypeScript', function () { tsconfigRootDir: path.resolve(__dirname, '../../files/typescript-export-react-test-renderer/'), }, }), + test({ + code: `import Foo from "./typescript-extended-config"`, + parser, + settings: { + 'import/parsers': { [parser]: ['.ts'] }, + 'import/resolver': { 'eslint-import-resolver-typescript': true }, + }, + parserOptions: { + tsconfigRootDir: path.resolve(__dirname, '../../files/typescript-extended-config/'), + }, + }), test({ code: `import foobar from "./typescript-export-assign-property"`, parser, From 62e2d883f6a6c57a71aa6594a79ea11ad4a5939a Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Mon, 16 Aug 2021 12:48:15 +0900 Subject: [PATCH 28/33] [New] Support `eslint` v8 --- .github/workflows/node-4+.yml | 21 +++++++++++++++++++++ .nycrc | 3 ++- CHANGELOG.md | 3 +++ package.json | 5 +++-- tests/dep-time-travel.sh | 8 ++++++++ tests/src/cli.js | 2 +- 6 files changed, 38 insertions(+), 4 deletions(-) diff --git a/.github/workflows/node-4+.yml b/.github/workflows/node-4+.yml index f2498807c..aaa11f3a0 100644 --- a/.github/workflows/node-4+.yml +++ b/.github/workflows/node-4+.yml @@ -26,6 +26,7 @@ jobs: matrix: node-version: ${{ fromJson(needs.matrix.outputs.latest) }} eslint: + - 8 - 7 - 6 - 5 @@ -44,24 +45,44 @@ jobs: env: TS_PARSER: 2 exclude: + - node-version: 15 + eslint: 8 + - node-version: 13 + eslint: 8 + - node-version: 11 + eslint: 8 + - node-version: 10 + eslint: 8 + - node-version: 9 + eslint: 8 - node-version: 9 eslint: 7 + - node-version: 8 + eslint: 8 - node-version: 8 eslint: 7 + - node-version: 7 + eslint: 8 - node-version: 7 eslint: 7 - node-version: 7 eslint: 6 + - node-version: 6 + eslint: 8 - node-version: 6 eslint: 7 - node-version: 6 eslint: 6 + - node-version: 5 + eslint: 8 - node-version: 5 eslint: 7 - node-version: 5 eslint: 6 - node-version: 5 eslint: 5 + - node-version: 4 + eslint: 8 - node-version: 4 eslint: 7 - node-version: 4 diff --git a/.nycrc b/.nycrc index a19f5b8b1..5d75e2157 100644 --- a/.nycrc +++ b/.nycrc @@ -13,6 +13,7 @@ "tests", "resolvers/*/test", "scripts", - "memo-parser" + "memo-parser", + "lib" ] } diff --git a/CHANGELOG.md b/CHANGELOG.md index 264b20c73..b7dc919a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ## [Unreleased] ### Added +- Support `eslint` v8 ([#2191], thanks [@ota-meshi]) - [`no-unresolved`]: add `caseSensitiveStrict` option ([#1262], thanks [@sergei-startsev]) - [`no-unused-modules`]: add eslint v8 support ([#2194], thanks [@coderaiser]) - [`no-restricted-paths`]: add/restore glob pattern support ([#2219], thanks [@stropho]) @@ -924,6 +925,7 @@ for info on changes for earlier releases. [#2212]: https://github.com/import-js/eslint-plugin-import/pull/2212 [#2196]: https://github.com/import-js/eslint-plugin-import/pull/2196 [#2194]: https://github.com/import-js/eslint-plugin-import/pull/2194 +[#2191]: https://github.com/import-js/eslint-plugin-import/pull/2191 [#2184]: https://github.com/import-js/eslint-plugin-import/pull/2184 [#2179]: https://github.com/import-js/eslint-plugin-import/pull/2179 [#2160]: https://github.com/import-js/eslint-plugin-import/pull/2160 @@ -1517,6 +1519,7 @@ for info on changes for earlier releases. [@noelebrun]: https://github.com/noelebrun [@ntdb]: https://github.com/ntdb [@nwalters512]: https://github.com/nwalters512 +[@ota-meshi]: https://github.com/ota-meshi [@panrafal]: https://github.com/panrafal [@paztis]: https://github.com/paztis [@pcorpet]: https://github.com/pcorpet diff --git a/package.json b/package.json index 08d5699f6..31d54e201 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,8 @@ "babylon": "^6.18.0", "chai": "^4.3.4", "cross-env": "^4.0.0", - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0", + "escope": "^3.6.0", + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint-import-resolver-node": "file:./resolvers/node", "eslint-import-resolver-typescript": "^1.0.2 || ^1.1.1", "eslint-import-resolver-webpack": "file:./resolvers/webpack", @@ -95,7 +96,7 @@ "typescript-eslint-parser": "^15 || ^22.0.0" }, "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0" + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" }, "dependencies": { "array-includes": "^3.1.3", diff --git a/tests/dep-time-travel.sh b/tests/dep-time-travel.sh index 2e2459576..116a4bfd4 100755 --- a/tests/dep-time-travel.sh +++ b/tests/dep-time-travel.sh @@ -34,3 +34,11 @@ if [[ "$TRAVIS_NODE_VERSION" -lt "8" ]]; then echo "Downgrading eslint-import-resolver-typescript..." npm i --no-save eslint-import-resolver-typescript@1.0.2 fi + +if [ "${ESLINT_VERSION}" = '8' ]; then + # This is a workaround for the crash in the initial processing of the ESLint class. + echo "Installing self" + npm i --no-save eslint-plugin-import@'.' -f + echo "Build self" + npm run build +fi diff --git a/tests/src/cli.js b/tests/src/cli.js index 9867e0498..5b7e705b0 100644 --- a/tests/src/cli.js +++ b/tests/src/cli.js @@ -97,7 +97,7 @@ describe('CLI regression tests', function () { }, ], errorCount: 1, - ...(semver.satisfies(eslintPkg.version, '>= 7.32 || ^8.0.0-0') && { + ...(semver.satisfies(eslintPkg.version, '>= 7.32 || ^8.0.0') && { fatalErrorCount: 0, }), warningCount: 0, From 0e857b69f860d60d7486462101d5378783e5f079 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 11 Oct 2021 09:30:13 -0700 Subject: [PATCH 29/33] [Deps] update `array-includes`, `array.prototype.flat`, `is-core-module`, `is-glob`, `object.values` --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 31d54e201..9d04b7aff 100644 --- a/package.json +++ b/package.json @@ -99,17 +99,17 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" }, "dependencies": { - "array-includes": "^3.1.3", - "array.prototype.flat": "^1.2.4", + "array-includes": "^3.1.4", + "array.prototype.flat": "^1.2.5", "debug": "^2.6.9", "doctrine": "^2.1.0", "eslint-import-resolver-node": "^0.3.6", "eslint-module-utils": "^2.6.2", "has": "^1.0.3", - "is-core-module": "^2.6.0", - "is-glob": "^4.0.1", + "is-core-module": "^2.7.0", + "is-glob": "^4.0.3", "minimatch": "^3.0.4", - "object.values": "^1.1.4", + "object.values": "^1.1.5", "resolve": "^1.20.0", "tsconfig-paths": "^3.11.0" } From c117be532152af7f55675607a92339361fe0968b Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 11 Oct 2021 09:37:01 -0700 Subject: [PATCH 30/33] [Dev Deps] update `array.prototype.flatmap`, `glob`; remove `babel-preset-es2015-argon` --- package.json | 5 ++--- resolvers/webpack/package.json | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 9d04b7aff..f7b730513 100644 --- a/package.json +++ b/package.json @@ -56,14 +56,13 @@ "@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", - "array.prototype.flatmap": "^1.2.4", + "array.prototype.flatmap": "^1.2.5", "babel-cli": "^6.26.0", "babel-core": "^6.26.3", "babel-eslint": "=8.0.3 || ^8.2.6", "babel-plugin-istanbul": "^4.1.6", "babel-plugin-module-resolver": "^2.7.1", "babel-preset-airbnb": "^2.6.0", - "babel-preset-es2015-argon": "latest", "babel-preset-flow": "^6.23.0", "babel-register": "^6.26.0", "babylon": "^6.18.0", @@ -80,7 +79,7 @@ "eslint-plugin-import": "2.x", "eslint-plugin-json": "^2.1.2", "fs-copy-file-sync": "^1.1.1", - "glob": "^7.1.7", + "glob": "^7.2.0", "in-publish": "^2.0.1", "linklocal": "^2.8.2", "lodash.isarray": "^4.0.0", diff --git a/resolvers/webpack/package.json b/resolvers/webpack/package.json index bd6269e2f..2afc84e14 100644 --- a/resolvers/webpack/package.json +++ b/resolvers/webpack/package.json @@ -48,7 +48,6 @@ }, "devDependencies": { "babel-plugin-istanbul": "^4.1.6", - "babel-preset-es2015-argon": "latest", "babel-register": "^6.26.0", "chai": "^3.5.0", "mocha": "^3.5.3", From 900ac9a1856c4222756ba95c1b25d7292ec718a4 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 11 Oct 2021 10:27:24 -0700 Subject: [PATCH 31/33] [resolvers/webpack] [deps] update `is-core-module` --- resolvers/webpack/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resolvers/webpack/package.json b/resolvers/webpack/package.json index 2afc84e14..0fceac591 100644 --- a/resolvers/webpack/package.json +++ b/resolvers/webpack/package.json @@ -36,7 +36,7 @@ "find-root": "^1.1.0", "has": "^1.0.3", "interpret": "^1.4.0", - "is-core-module": "^2.6.0", + "is-core-module": "^2.7.0", "is-regex": "^1.1.4", "lodash": "^4.17.21", "resolve": "^1.20.0", From 7463de2aa3389ffb40dee465b1ec763bc4382361 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 11 Oct 2021 10:52:39 -0700 Subject: [PATCH 32/33] utils: v2.7.0 --- utils/CHANGELOG.md | 2 ++ utils/package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/utils/CHANGELOG.md b/utils/CHANGELOG.md index 7d08f1963..fe7e35a3d 100644 --- a/utils/CHANGELOG.md +++ b/utils/CHANGELOG.md @@ -5,6 +5,8 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ## Unreleased +## v2.7.0 - 2021-10-11 + ### Added - `fileExistsWithCaseSync`: add `strict` argument ([#1262], thanks [@sergei-startsev]) - add `visit`, to support dynamic imports ([#1660], [#2212], thanks [@maxkomarychev], [@aladdin-add], [@Hypnosphi]) diff --git a/utils/package.json b/utils/package.json index 787ce83a7..523f2d8d1 100644 --- a/utils/package.json +++ b/utils/package.json @@ -1,6 +1,6 @@ { "name": "eslint-module-utils", - "version": "2.6.2", + "version": "2.7.0", "description": "Core utilities to support eslint-plugin-import and other module-related plugins.", "engines": { "node": ">=4" From b0131d2757bb28e8e1eb3f34670a2c59a3d7b9ff Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Mon, 11 Oct 2021 13:32:21 -0700 Subject: [PATCH 33/33] Bump to v2.25.0 --- CHANGELOG.md | 5 ++++- package.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7dc919a4..0d1283d3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel ## [Unreleased] +## [2.25.0] - 2021-10-11 + ### Added - Support `eslint` v8 ([#2191], thanks [@ota-meshi]) - [`no-unresolved`]: add `caseSensitiveStrict` option ([#1262], thanks [@sergei-startsev]) @@ -1295,7 +1297,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.24.2...HEAD +[Unreleased]: https://github.com/import-js/eslint-plugin-import/compare/v2.25.0...HEAD +[2.25.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.24.2...v2.25.0 [2.24.2]: https://github.com/import-js/eslint-plugin-import/compare/v2.24.1...v2.24.2 [2.24.1]: https://github.com/import-js/eslint-plugin-import/compare/v2.24.0...v2.24.1 [2.24.0]: https://github.com/import-js/eslint-plugin-import/compare/v2.23.4...v2.24.0 diff --git a/package.json b/package.json index f7b730513..9b09a70de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-import", - "version": "2.24.2", + "version": "2.25.0", "description": "Import with sanity.", "engines": { "node": ">=4"