Skip to content

Commit

Permalink
[Fix] Use context.getPhysicalFilename() when available (ESLint 7.28+)
Browse files Browse the repository at this point in the history
  • Loading branch information
pmcelhaney authored and ljharb committed Jul 21, 2021
1 parent 3761435 commit 2f0243f
Show file tree
Hide file tree
Showing 17 changed files with 175 additions and 19 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -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])
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/core/packagePath.js
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/rules/named.js
Expand Up @@ -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],
Expand Down
6 changes: 3 additions & 3 deletions src/rules/newline-after-import.js
Expand Up @@ -48,7 +48,7 @@ function isExportDefaultClass(node) {
}

function isExportNameClass(node) {

return node.type === 'ExportNamedDeclaration' && node.declaration && node.declaration.type === 'ClassDeclaration';
}

Expand Down Expand Up @@ -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;
Expand All @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-cycle.js
Expand Up @@ -37,7 +37,7 @@ module.exports = {
},

create: function (context) {
const myPath = context.getFilename();
const myPath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename();
if (myPath === '<text>') return {}; // can't cycle-check a non-file

const options = context.options[0] || {};
Expand Down
4 changes: 2 additions & 2 deletions src/rules/no-extraneous-dependencies.js
Expand Up @@ -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
)
);
}
Expand Down Expand Up @@ -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 = {
Expand Down
4 changes: 2 additions & 2 deletions src/rules/no-import-module-exports.js
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-relative-packages.js
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-relative-parent-imports.js
Expand Up @@ -15,7 +15,7 @@ module.exports = {
},

create: function noRelativePackages(context) {
const myPath = context.getFilename();
const myPath = context.getPhysicalFilename ? context.getPhysicalFilename() : context.getFilename();
if (myPath === '<text>') return {}; // can't check a non-file

function checkSourceValue(sourceNode) {
Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-restricted-paths.js
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-self-import.js
Expand Up @@ -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 !== '<text>' && filePath === resolve(requireName, context)) {
Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-unassigned-import.js
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-unused-modules.js
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/rules/no-useless-path-segments.js
Expand Up @@ -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) {
Expand Down
3 changes: 2 additions & 1 deletion tests/src/core/getExports.js
Expand Up @@ -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',
};
Expand Down
152 changes: 152 additions & 0 deletions tests/src/core/resolve.js
Expand Up @@ -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<stack-was-here>');
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<stack-was-here>');
expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 });
});
});

const caseDescribe = (!CASE_SENSITIVE_FS ? describe : describe.skip);
caseDescribe('case sensitivity', function () {
let file;
Expand Down
2 changes: 1 addition & 1 deletion utils/resolve.js
Expand Up @@ -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`.
Expand Down

0 comments on commit 2f0243f

Please sign in to comment.