From 7d5e5f68849ae80caec0fc96ecceebccd348deec Mon Sep 17 00:00:00 2001 From: Francesco Trotta Date: Wed, 13 Dec 2023 17:09:00 +0100 Subject: [PATCH] fix: `TypeError: fs.exists is not a function` on read-only file system (#17846) --- lib/eslint/flat-eslint.js | 3 ++- tests/lib/eslint/eslint.js | 35 +++++++++++++++++++++++++++++++++ tests/lib/eslint/flat-eslint.js | 34 ++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/lib/eslint/flat-eslint.js b/lib/eslint/flat-eslint.js index 306c80de1d6..b44777d71bc 100644 --- a/lib/eslint/flat-eslint.js +++ b/lib/eslint/flat-eslint.js @@ -11,6 +11,7 @@ // Note: Node.js 12 does not support fs/promises. const fs = require("fs").promises; +const { existsSync } = require("fs"); const path = require("path"); const findUp = require("find-up"); const { version } = require("../../package.json"); @@ -766,7 +767,7 @@ class FlatESLint { const errorCode = error && error.code; // Ignore errors when no such file exists or file system is read only (and cache file does not exist) - if (errorCode !== "ENOENT" && !(errorCode === "EROFS" && !(await fs.exists(cacheFilePath)))) { + if (errorCode !== "ENOENT" && !(errorCode === "EROFS" && !existsSync(cacheFilePath))) { throw error; } } diff --git a/tests/lib/eslint/eslint.js b/tests/lib/eslint/eslint.js index 0753cf685c2..040722fcf64 100644 --- a/tests/lib/eslint/eslint.js +++ b/tests/lib/eslint/eslint.js @@ -2711,6 +2711,41 @@ describe("ESLint", () => { assert(!shell.test("-f", cacheFilePath), "the cache for eslint should have been deleted since last run did not use the cache"); }); + it("should not throw an error if the cache file to be deleted does not exist on a read-only file system", async () => { + cacheFilePath = getFixturePath(".eslintcache"); + doDelete(cacheFilePath); + assert(!shell.test("-f", cacheFilePath), "the cache file already exists and wasn't successfully deleted"); + + // Simulate a read-only file system. + sinon.stub(fs, "unlinkSync").throws( + Object.assign(new Error("read-only file system"), { code: "EROFS" }) + ); + + const eslintOptions = { + useEslintrc: false, + + // specifying cache true the cache will be created + cache: false, + cacheLocation: cacheFilePath, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + extensions: ["js"], + cwd: path.join(fixtureDir, "..") + }; + + eslint = new ESLint(eslintOptions); + + const file = getFixturePath("cache/src", "test-file.js"); + + await eslint.lintFiles([file]); + + assert(fs.unlinkSync.calledWithExactly(cacheFilePath), "Expected attempt to delete the cache was not made."); + }); + it("should store in the cache a file that has lint messages and a file that doesn't have lint messages", async () => { cacheFilePath = getFixturePath(".eslintcache"); doDelete(cacheFilePath); diff --git a/tests/lib/eslint/flat-eslint.js b/tests/lib/eslint/flat-eslint.js index 443452bc096..4311537387f 100644 --- a/tests/lib/eslint/flat-eslint.js +++ b/tests/lib/eslint/flat-eslint.js @@ -2601,6 +2601,40 @@ describe("FlatESLint", () => { assert(!shell.test("-f", cacheFilePath), "the cache for eslint should have been deleted since last run did not use the cache"); }); + it("should not throw an error if the cache file to be deleted does not exist on a read-only file system", async () => { + cacheFilePath = getFixturePath(".eslintcache"); + doDelete(cacheFilePath); + assert(!shell.test("-f", cacheFilePath), "the cache file already exists and wasn't successfully deleted"); + + // Simulate a read-only file system. + sinon.stub(fsp, "unlink").rejects( + Object.assign(new Error("read-only file system"), { code: "EROFS" }) + ); + + const eslintOptions = { + overrideConfigFile: true, + + // specifying cache false the cache will be deleted + cache: false, + cacheLocation: cacheFilePath, + overrideConfig: { + rules: { + "no-console": 0, + "no-unused-vars": 2 + } + }, + cwd: path.join(fixtureDir, "..") + }; + + eslint = new FlatESLint(eslintOptions); + + const file = getFixturePath("cache/src", "test-file.js"); + + await eslint.lintFiles([file]); + + assert(fsp.unlink.calledWithExactly(cacheFilePath), "Expected attempt to delete the cache was not made."); + }); + it("should store in the cache a file that has lint messages and a file that doesn't have lint messages", async () => { cacheFilePath = getFixturePath(".eslintcache"); doDelete(cacheFilePath);