diff --git a/CHANGELOG.md b/CHANGELOG.md index 294529304a..e8173955f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel - [`no-duplicates`]: ensure autofix avoids excessive newlines ([#2028], thanks [@ertrzyiks]) - [`extensions`]: avoid crashing on partially typed import/export statements ([#2118], thanks [@ljharb]) - [`no-extraneous-dependencies`]: add ESM intermediate package.json support] ([#2121], thanks [@paztis]) + - Use `context.getPhysicalFilename()` when available (ESLint 7.28+) ([#2160], thanks [@pmcelhaney]) ### Changed - [Docs] `extensions`: removed incorrect cases ([#2138], thanks [@wenfangdu]) @@ -809,6 +810,7 @@ for info on changes for earlier releases. [`memo-parser`]: ./memo-parser/README.md +[#2160]: https://github.com/benmosher/eslint-plugin-import/pull/2160 [#2158]: https://github.com/benmosher/eslint-plugin-import/pull/2158 [#2138]: https://github.com/benmosher/eslint-plugin-import/pull/2138 [#2121]: https://github.com/benmosher/eslint-plugin-import/pull/2121 @@ -1374,6 +1376,7 @@ for info on changes for earlier releases. [@paztis]: https://github.com/paztis [@pcorpet]: https://github.com/pcorpet [@Pessimistress]: https://github.com/Pessimistress +[@pmcelhaney]: https://github.com/pmcelhaney [@preco21]: https://github.com/preco21 [@pzhine]: https://github.com/pzhine [@ramasilveyra]: https://github.com/ramasilveyra diff --git a/src/core/packagePath.js b/src/core/packagePath.js index 315ec0918c..a8c3c67632 100644 --- a/src/core/packagePath.js +++ b/src/core/packagePath.js @@ -4,7 +4,7 @@ import readPkgUp from 'read-pkg-up'; export function getContextPackagePath(context) { - return getFilePackagePath(context.getFilename()); + return getFilePackagePath(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()); } export function getFilePackagePath(filePath) { diff --git a/src/rules/named.js b/src/rules/named.js index c4ce5ea915..7dee06ce10 100644 --- a/src/rules/named.js +++ b/src/rules/named.js @@ -43,7 +43,7 @@ module.exports = { if (!deepLookup.found) { if (deepLookup.path.length > 1) { const deepPath = deepLookup.path - .map(i => path.relative(path.dirname(context.getFilename()), i.path)) + .map(i => path.relative(path.dirname(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()), i.path)) .join(' -> '); context.report(im[key], diff --git a/src/rules/newline-after-import.js b/src/rules/newline-after-import.js index f9a817846b..cf5dce831e 100644 --- a/src/rules/newline-after-import.js +++ b/src/rules/newline-after-import.js @@ -48,7 +48,7 @@ function isExportDefaultClass(node) { } function isExportNameClass(node) { - + return node.type === 'ExportNamedDeclaration' && node.declaration && node.declaration.type === 'ClassDeclaration'; } @@ -124,7 +124,7 @@ after ${type} statement not followed by another ${type}.`, const { parent } = node; const nodePosition = parent.body.indexOf(node); const nextNode = parent.body[nodePosition + 1]; - + // skip "export import"s if (node.type === 'TSImportEqualsDeclaration' && node.isExport) { return; @@ -144,7 +144,7 @@ after ${type} statement not followed by another ${type}.`, } }, 'Program:exit': function () { - log('exit processing for', context.getFilename()); + log('exit processing for', context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()); const scopeBody = getScopeBody(context.getScope()); log('got scope:', scopeBody); diff --git a/src/rules/no-cycle.js b/src/rules/no-cycle.js index 9d9a28cd66..ec4cfeae9a 100644 --- a/src/rules/no-cycle.js +++ b/src/rules/no-cycle.js @@ -37,7 +37,7 @@ module.exports = { }, create: function (context) { - const myPath = context.getFilename(); + const myPath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); if (myPath === '') return {}; // can't cycle-check a non-file const options = context.options[0] || {}; diff --git a/src/rules/no-extraneous-dependencies.js b/src/rules/no-extraneous-dependencies.js index 7e46ac34bc..9403931f53 100644 --- a/src/rules/no-extraneous-dependencies.js +++ b/src/rules/no-extraneous-dependencies.js @@ -69,7 +69,7 @@ function getDependencies(context, packageDir) { Object.assign( packageContent, extractDepFields( - readPkgUp.sync({ cwd: context.getFilename(), normalize: false }).pkg + readPkgUp.sync({ cwd: context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(), normalize: false }).pkg ) ); } @@ -254,7 +254,7 @@ module.exports = { create: function (context) { const options = context.options[0] || {}; - const filename = context.getFilename(); + const filename = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); const deps = getDependencies(context, options.packageDir) || extractDepFields({}); const depsOptions = { diff --git a/src/rules/no-import-module-exports.js b/src/rules/no-import-module-exports.js index 8ce5f4c9ac..50ba212c87 100644 --- a/src/rules/no-import-module-exports.js +++ b/src/rules/no-import-module-exports.js @@ -3,7 +3,7 @@ import path from 'path'; import pkgUp from 'pkg-up'; function getEntryPoint(context) { - const pkgPath = pkgUp.sync(context.getFilename()); + const pkgPath = pkgUp.sync(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()); try { return require.resolve(path.dirname(pkgPath)); } catch (error) { @@ -39,7 +39,7 @@ module.exports = { let alreadyReported = false; function report(node) { - const fileName = context.getFilename(); + const fileName = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); const isEntryPoint = entryPoint === fileName; const isIdentifier = node.object.type === 'Identifier'; const hasKeywords = (/^(module|exports)$/).test(node.object.name); diff --git a/src/rules/no-relative-packages.js b/src/rules/no-relative-packages.js index a654c08393..90c1ecc701 100644 --- a/src/rules/no-relative-packages.js +++ b/src/rules/no-relative-packages.js @@ -21,7 +21,7 @@ function checkImportForRelativePackage(context, importPath, node) { } const resolvedImport = resolve(importPath, context); - const resolvedContext = context.getFilename(); + const resolvedContext = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); if (!resolvedImport || !resolvedContext) { return; diff --git a/src/rules/no-relative-parent-imports.js b/src/rules/no-relative-parent-imports.js index 9826da826b..8e3696275b 100644 --- a/src/rules/no-relative-parent-imports.js +++ b/src/rules/no-relative-parent-imports.js @@ -15,7 +15,7 @@ module.exports = { }, create: function noRelativePackages(context) { - const myPath = context.getFilename(); + const myPath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); if (myPath === '') return {}; // can't check a non-file function checkSourceValue(sourceNode) { diff --git a/src/rules/no-restricted-paths.js b/src/rules/no-restricted-paths.js index 6409ff57ac..058aa43ea8 100644 --- a/src/rules/no-restricted-paths.js +++ b/src/rules/no-restricted-paths.js @@ -52,7 +52,7 @@ module.exports = { const options = context.options[0] || {}; const restrictedPaths = options.zones || []; const basePath = options.basePath || process.cwd(); - const currentFilename = context.getFilename(); + const currentFilename = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); const matchingZones = restrictedPaths.filter((zone) => { const targetPath = path.resolve(basePath, zone.target); diff --git a/src/rules/no-self-import.js b/src/rules/no-self-import.js index a10be56786..58c393b662 100644 --- a/src/rules/no-self-import.js +++ b/src/rules/no-self-import.js @@ -8,7 +8,7 @@ import moduleVisitor from 'eslint-module-utils/moduleVisitor'; import docsUrl from '../docsUrl'; function isImportingSelf(context, node, requireName) { - const filePath = context.getFilename(); + const filePath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); // If the input is from stdin, this test can't fail if (filePath !== '' && filePath === resolve(requireName, context)) { diff --git a/src/rules/no-unassigned-import.js b/src/rules/no-unassigned-import.js index ed292e9128..37be903e0b 100644 --- a/src/rules/no-unassigned-import.js +++ b/src/rules/no-unassigned-import.js @@ -32,7 +32,7 @@ function testIsAllow(globs, filename, source) { function create(context) { const options = context.options[0] || {}; - const filename = context.getFilename(); + const filename = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); const isAllow = source => testIsAllow(options.allow, filename, source); return { diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index 99b564edab..205259eef9 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -463,7 +463,7 @@ module.exports = { doPreparation(src, ignoreExports, context); } - const file = context.getFilename(); + const file = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(); const checkExportPresence = node => { if (!missingExports) { diff --git a/src/rules/no-useless-path-segments.js b/src/rules/no-useless-path-segments.js index b22aa94788..a058608028 100644 --- a/src/rules/no-useless-path-segments.js +++ b/src/rules/no-useless-path-segments.js @@ -58,7 +58,7 @@ module.exports = { }, create(context) { - const currentDir = path.dirname(context.getFilename()); + const currentDir = path.dirname(context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename()); const options = context.options[0]; function checkSourceValue(source) { diff --git a/tests/src/core/getExports.js b/tests/src/core/getExports.js index 5a9bdadb15..fc5978bfad 100644 --- a/tests/src/core/getExports.js +++ b/tests/src/core/getExports.js @@ -12,7 +12,8 @@ import * as unambiguous from 'eslint-module-utils/unambiguous'; describe('ExportMap', function () { const fakeContext = { - getFilename: getFilename, + getFilename: function () { throw new Error('Should call getPhysicalFilename() instead of getFilename()'); }, + getPhysicalFilename: getFilename, settings: {}, parserPath: 'babel-eslint', }; diff --git a/tests/src/core/resolve.js b/tests/src/core/resolve.js index b8deaa6d25..2e8896b8dd 100644 --- a/tests/src/core/resolve.js +++ b/tests/src/core/resolve.js @@ -162,6 +162,158 @@ describe('resolve', function () { expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 }); }); + describe('getPhysicalFilename()', () => { // context.getPhysicalFilename() is available in ESLint 7.28+ + + function unexpectedCallToGetFilename() { + throw new Error('Expected to call to getPhysicalFilename() instead of getFilename()'); + } + + it('resolves via a custom resolver with interface version 1', function () { + const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-v1' }); + + expect(resolve( '../files/foo' + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { 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'); } }), + )).to.equal(undefined); + + expect(resolve( '../files/not-found' + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('not-found.js'); } }), + )).to.equal(undefined); + }); + + it('resolves via a custom resolver with interface version 1 assumed if not specified', function () { + const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-no-version' }); + + expect(resolve( '../files/foo' + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { 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'); } }), + )).to.equal(undefined); + + expect(resolve( '../files/not-found' + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('not-found.js'); } }), + )).to.equal(undefined); + }); + + it('resolves via a custom resolver with interface version 2', function () { + const testContext = utils.testContext({ 'import/resolver': './foo-bar-resolver-v2' }); + const testContextReports = []; + testContext.report = function (reportInfo) { + testContextReports.push(reportInfo); + }; + + expect(resolve( '../files/foo' + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { 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'); } }), + )).to.equal(undefined); + expect(testContextReports[0]).to.be.an('object'); + expect(replaceErrorStackForTest(testContextReports[0].message)).to.equal('Resolve error: foo-bar-resolver-v2 resolve test exception\n'); + expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 }); + + testContextReports.length = 0; + expect(resolve( '../files/not-found' + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('not-found.js'); } }), + )).to.equal(undefined); + expect(testContextReports.length).to.equal(0); + }); + + it('respects import/resolver as array of strings', function () { + const testContext = utils.testContext({ 'import/resolver': [ './foo-bar-resolver-v2', './foo-bar-resolver-v1' ] }); + + expect(resolve( '../files/foo' + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('foo.js'); } }), + )).to.equal(utils.testFilePath('./bar.jsx')); + }); + + it('respects import/resolver as object', function () { + const testContext = utils.testContext({ 'import/resolver': { './foo-bar-resolver-v2': {} } }); + + expect(resolve( '../files/foo' + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('foo.js'); } }), + )).to.equal(utils.testFilePath('./bar.jsx')); + }); + + it('respects import/resolver as array of objects', function () { + const testContext = utils.testContext({ 'import/resolver': [ { './foo-bar-resolver-v2': {} }, { './foo-bar-resolver-v1': {} } ] }); + + expect(resolve( '../files/foo' + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('foo.js'); } }), + )).to.equal(utils.testFilePath('./bar.jsx')); + }); + + it('finds resolvers from the source files rather than eslint-module-utils', function () { + const testContext = utils.testContext({ 'import/resolver': { 'foo': {} } }); + + expect(resolve( '../files/foo' + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { return utils.getFilename('foo.js'); } }), + )).to.equal(utils.testFilePath('./bar.jsx')); + }); + + it('reports invalid import/resolver config', function () { + const testContext = utils.testContext({ 'import/resolver': 123.456 }); + const testContextReports = []; + testContext.report = function (reportInfo) { + testContextReports.push(reportInfo); + }; + + testContextReports.length = 0; + expect(resolve( '../files/foo' + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { 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'); + expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 }); + }); + + it('reports loaded resolver with invalid interface', function () { + const resolverName = './foo-bar-resolver-invalid'; + const testContext = utils.testContext({ 'import/resolver': resolverName }); + const testContextReports = []; + testContext.report = function (reportInfo) { + testContextReports.push(reportInfo); + }; + testContextReports.length = 0; + expect(resolve( '../files/foo' + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { 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`); + expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 }); + }); + + it('respects import/resolve extensions', function () { + const testContext = utils.testContext({ 'import/resolve': { 'extensions': ['.jsx'] } }); + + expect(resolve( './jsx/MyCoolComponent' + , testContext, + )).to.equal(utils.testFilePath('./jsx/MyCoolComponent.jsx')); + }); + + it('reports load exception in a user resolver', function () { + const testContext = utils.testContext({ 'import/resolver': './load-error-resolver' }); + const testContextReports = []; + testContext.report = function (reportInfo) { + testContextReports.push(reportInfo); + }; + + expect(resolve( '../files/exception' + , Object.assign({}, testContext, { getFilename: unexpectedCallToGetFilename, getPhysicalFilename: function () { 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'); + expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 }); + }); + }); + const caseDescribe = (!CASE_SENSITIVE_FS ? describe : describe.skip); caseDescribe('case sensitivity', function () { let file; diff --git a/utils/resolve.js b/utils/resolve.js index ab9f13d5e8..f488ea798f 100644 --- a/utils/resolve.js +++ b/utils/resolve.js @@ -217,7 +217,7 @@ const erroredContexts = new Set(); */ function resolve(p, context) { try { - return relative(p, context.getFilename(), context.settings); + return relative(p, context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename(), context.settings); } catch (err) { if (!erroredContexts.has(context)) { // The `err.stack` string starts with `err.name` followed by colon and `err.message`.