Repository: yannickcr/eslint-plugin-react
Pull request #2965 (2021-04-13 10:08:27)

[Fix] version detection: support recursive processor virtual filename
continue to work on #2949

---

According to https://github.com/eslint/eslint/pull/14227#discussion_r602802758, virtual filename could be recursive, this PR fix this possible issue.

JounQin: > if we're sure there's no chance of an infinite loop here, lgtm

Sure, that error indicates the filename is a virtual filename and what means a true filename must exist.
JounQin: @ljharb Any luck to merge?
JounQin: @ljharb Any luck to merge?

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b68294fb..a3fdb5b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
 ### Added
 * component detection: add componentWrapperFunctions setting ([#2713][] @@jzabala @LandonSchropp)
 * [`no-unused-prop-types`]: add ignore option ([#2972][] @grit96)
+* version detection: support recursive processor virtual filename ([#2965][] @JounQin)
 
 ### Fixed
 * [`jsx-handler-names`]: properly substitute value into message ([#2975][] @G-Rath)
@@ -32,6 +33,7 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
 [#2975]: https://github.com/yannickcr/eslint-plugin-react/pull/2975
 [#2974]: https://github.com/yannickcr/eslint-plugin-react/pull/2974
 [#2972]: https://github.com/yannickcr/eslint-plugin-react/pull/2972
+[#2965]: https://github.com/yannickcr/eslint-plugin-react/pull/2965
 [#2713]: https://github.com/yannickcr/eslint-plugin-react/pull/2713
 
 ## [7.23.2] - 2021.04.08
@@ -3366,4 +3368,4 @@ If you're still not using React 15 you can keep the old behavior by setting the
 [`function-component-definition`]: docs/rules/function-component-definition.md
 [`jsx-newline`]: docs/rules/jsx-newline.md
 [`jsx-no-constructed-context-values`]: docs/rules/jsx-no-constructed-context-values.md
-[`no-unstable-nested-components`]: docs/rules/no-unstable-nested-components.md
\ No newline at end of file
+[`no-unstable-nested-components`]: docs/rules/no-unstable-nested-components.md
diff --git a/lib/util/version.js b/lib/util/version.js
index 8534f100..b778c8b9 100644
--- a/lib/util/version.js
+++ b/lib/util/version.js
@@ -22,25 +22,24 @@ function resetDetectedVersion() {
   cachedDetectedReactVersion = undefined;
 }
 
-function resolveBasedir(context) {
-  let basedir = process.cwd();
-  if (context) {
-    const filename = context.getFilename();
+function resolveBasedir(contextOrFilename) {
+  if (contextOrFilename) {
+    const filename = typeof contextOrFilename === 'string' ? contextOrFilename : contextOrFilename.getFilename();
     const dirname = path.dirname(filename);
     try {
       if (fs.statSync(filename).isFile()) {
         // dirname must be dir here
-        basedir = dirname;
+        return 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);
+        // virtual filename could be recursive
+        return resolveBasedir(dirname);
       }
     }
   }
-  return basedir;
+  return process.cwd();
 }
 
 // TODO, semver-major: remove context fallback
diff --git a/package.json b/package.json
index 620db84f..5c2dd263 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
     "string.prototype.matchall": "^4.0.4"
   },
   "devDependencies": {
-    "@types/eslint": "^7.2.8",
+    "@types/eslint": "=7.2.10",
     "@types/estree": "^0.0.47",
     "@types/node": "^14.14.37",
     "@typescript-eslint/parser": "^2.34.0",


Test patch:
diff --git a/tests/util/version.js b/tests/util/version.js
index bd08f292..f43c603f 100644
--- a/tests/util/version.js
+++ b/tests/util/version.js
@@ -87,6 +87,14 @@ describe('Version', () => {
       assert.equal(versionUtil.testReactVersion(context, '2.3.5'), false);
       assert.equal(versionUtil.testFlowVersion(context, '2.92.0'), true);
     });
+
+    it('works with recursive virtual filename', () => {
+      sinon.stub(context, 'getFilename').callsFake(() => path.resolve(base, 'detect-version-sibling', 'test.js/0_fake.md/1_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', () => {