Repository: yannickcr/eslint-plugin-react (js)

Issue:
`lib/util/version.js:31:31` is not compatible with processors with virtual filename
A lot of rules are incompatible.

```log
Oops! Something went wrong! :(

ESLint: 7.22.0

TypeError: Error while loading rule 'react/display-name': Provided basedir "/Users/JounQin/Workspaces/GitHub/eslint-mdx/README.md" is not a directory, or a symlink to a directory
Occurred while linting /Users/JounQin/Workspaces/GitHub/eslint-mdx/README.md/3_3.js
    at Function.resolveSync [as sync] (/Users/JounQin/Workspaces/GitHub/eslint-mdx/node_modules/eslint-plugin-react/node_modules/resolve/lib/sync.js:91:24)
    at detectReactVersion (/Users/JounQin/Workspaces/GitHub/eslint-mdx/node_modules/eslint-plugin-react/lib/util/version.js:31:31)
    at getReactVersionFromContext (/Users/JounQin/Workspaces/GitHub/eslint-mdx/node_modules/eslint-plugin-react/lib/util/version.js:55:25)
    at Object.testReactVersion (/Users/JounQin/Workspaces/GitHub/eslint-mdx/node_modules/eslint-plugin-react/lib/util/version.js:124:35)
    at usedPropTypesInstructions (/Users/JounQin/Workspaces/GitHub/eslint-mdx/node_modules/eslint-plugin-react/lib/util/usedPropTypes.js:285:48)
    at Function.componentRule (/Users/JounQin/Workspaces/GitHub/eslint-mdx/node_modules/eslint-plugin-react/lib/util/Components.js:966:37)
    at createRuleListeners (/Users/JounQin/Workspaces/GitHub/eslint-mdx/node_modules/eslint/lib/linter/linter.js:761:21)
    at /Users/JounQin/Workspaces/GitHub/eslint-mdx/node_modules/eslint/lib/linter/linter.js:931:31
    at Array.forEach (<anonymous>)
    at runRules (/Users/JounQin/Workspaces/GitHub/eslint-mdx/node_modules/eslint/lib/linter/linter.js:876:34)
```

Can we add a check like following?

https://github.com/prettier/eslint-plugin-prettier/pull/401/files#diff-e9a90fe7b2d3420b8e5f64e24d5f56ffe6383e47b07461172cc29ed21b768905R79-R94


Pull request description:
[Fix] version detection: support processor virtual filename
close #2948

cc @ljharb 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b2311108..2227b293 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,9 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
 
 ## Unreleased
 
+### Fixed
+* version detection: support processor virtual filename ([#2949][] @JounQin)
+
 ## [7.23.0] - 2021.03.22
 
 ### Added
@@ -33,6 +36,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
 * [readme] Replace lists of rules with tables in readme ([#2908][] @motato1)
 * [Docs] added missing curly braces ([#2923][] @Muditxofficial)
 
+[#2949]: https://github.com/yannickcr/eslint-plugin-react/pull/2949
 [#2943]: https://github.com/yannickcr/eslint-plugin-react/pull/2943
 [#2935]: https://github.com/yannickcr/eslint-plugin-react/pull/2935
 [#2933]: https://github.com/yannickcr/eslint-plugin-react/pull/2933
diff --git a/lib/util/version.js b/lib/util/version.js
index c34b7c55..8534f100 100644
--- a/lib/util/version.js
+++ b/lib/util/version.js
@@ -5,6 +5,7 @@
 
 'use strict';
 
+const fs = require('fs');
 const resolve = require('resolve');
 const path = require('path');
 const error = require('./error');
@@ -21,14 +22,37 @@ function resetDetectedVersion() {
   cachedDetectedReactVersion = undefined;
 }
 
+function resolveBasedir(context) {
+  let basedir = process.cwd();
+  if (context) {
+    const filename = context.getFilename();
+    const dirname = path.dirname(filename);
+    try {
+      if (fs.statSync(filename).isFile()) {
+        // dirname must be dir here
+        basedir = dirname;
+      }
+    } catch (err) {
+      // https://github.com/eslint/eslint/issues/11989
+      if (err.code === 'ENOTDIR') {
+        // the error code alreay indicates that dirname is a file
+        basedir = path.dirname(dirname);
+      }
+    }
+  }
+  return basedir;
+}
+
 // TODO, semver-major: remove context fallback
 function detectReactVersion(context) {
   if (cachedDetectedReactVersion) {
     return cachedDetectedReactVersion;
   }
 
+  const basedir = resolveBasedir(context);
+
   try {
-    const reactPath = resolve.sync('react', {basedir: context ? path.dirname(context.getFilename()) : process.cwd()});
+    const reactPath = resolve.sync('react', {basedir});
     const react = require(reactPath); // eslint-disable-line global-require, import/no-dynamic-require
     cachedDetectedReactVersion = react.version;
     return cachedDetectedReactVersion;
@@ -70,8 +94,10 @@ function getReactVersionFromContext(context) {
 
 // TODO, semver-major: remove context fallback
 function detectFlowVersion(context) {
+  const basedir = resolveBasedir(context);
+
   try {
-    const flowPackageJsonPath = resolve.sync('flow-bin/package.json', {basedir: context ? path.dirname(context.getFilename()) : process.cwd()});
+    const flowPackageJsonPath = resolve.sync('flow-bin/package.json', {basedir});
     const flowPackageJson = require(flowPackageJsonPath); // eslint-disable-line global-require, import/no-dynamic-require
     return flowPackageJson.version;
   } catch (e) {


Test patch:
diff --git a/tests/util/version.js b/tests/util/version.js
index 226ba65f..bd08f292 100644
--- a/tests/util/version.js
+++ b/tests/util/version.js
@@ -79,6 +79,14 @@ describe('Version', () => {
         ['Warning: Flow version was set to "detect" in eslint-plugin-react settings, but the "flow-bin" package is not installed. Assuming latest Flow version for linting.']
       ];
     });
+
+    it('works with virtual filename', () => {
+      sinon.stub(context, 'getFilename').callsFake(() => path.resolve(base, 'detect-version-sibling', 'test.js/0_fake.js'));
+
+      assert.equal(versionUtil.testReactVersion(context, '2.3.4'), true);
+      assert.equal(versionUtil.testReactVersion(context, '2.3.5'), false);
+      assert.equal(versionUtil.testFlowVersion(context, '2.92.0'), true);
+    });
   });
 
   describe('string version', () => {