Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use getPhysicalFilename() instead of getFilename() if available (ESLint 7.28+) #2160

Merged
merged 1 commit into from Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
17 changes: 12 additions & 5 deletions tests/src/core/getExports.js
Expand Up @@ -11,11 +11,18 @@ import { getFilename } from '../utils';
import * as unambiguous from 'eslint-module-utils/unambiguous';

describe('ExportMap', function () {
const fakeContext = {
getFilename: getFilename,
settings: {},
parserPath: 'babel-eslint',
};
const fakeContext = Object.assign(
semver.satisfies(eslintPkg.version, '>= 7.28') ? {
getFilename: function () { throw new Error('Should call getPhysicalFilename() instead of getFilename()'); },
getPhysicalFilename: getFilename,
} : {
getFilename,
},
{
settings: {},
parserPath: 'babel-eslint',
},
);

it('handles ExportAllDeclaration', function () {
let imports;
Expand Down
154 changes: 154 additions & 0 deletions tests/src/core/resolve.js
@@ -1,4 +1,6 @@
import { expect } from 'chai';
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';
Expand Down Expand Up @@ -162,6 +164,158 @@ describe('resolve', function () {
expect(testContextReports[0].loc).to.eql({ line: 1, column: 0 });
});

// context.getPhysicalFilename() is available in ESLint 7.28+
(semver.satisfies(eslintPkg.version, '>= 7.28') ? describe : describe.skip)('getPhysicalFilename()', () => {
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