Skip to content

Commit

Permalink
fix: don't crash when fs.readFile returns promise from another realm (
Browse files Browse the repository at this point in the history
#18416)

* fix: don't crash when `fs.readFile` returns promise from another realm

Fixes #18407

* update test case title
  • Loading branch information
mdjermanovic committed May 7, 2024
1 parent 040700a commit 37eba48
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 16 deletions.
3 changes: 1 addition & 2 deletions lib/eslint/eslint.js
Expand Up @@ -9,8 +9,7 @@
// Requirements
//------------------------------------------------------------------------------

// Note: Node.js 12 does not support fs/promises.
const fs = require("fs").promises;
const fs = require("fs/promises");
const { existsSync } = require("fs");
const path = require("path");
const findUp = require("find-up");
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -72,7 +72,7 @@
"@eslint/js": "9.2.0",
"@humanwhocodes/config-array": "^0.13.0",
"@humanwhocodes/module-importer": "^1.0.1",
"@humanwhocodes/retry": "^0.2.3",
"@humanwhocodes/retry": "^0.2.4",
"@nodelib/fs.walk": "^1.2.8",
"ajv": "^6.12.4",
"chalk": "^4.0.0",
Expand Down
76 changes: 63 additions & 13 deletions tests/lib/eslint/eslint.js
Expand Up @@ -13,7 +13,7 @@
const assert = require("assert");
const util = require("util");
const fs = require("fs");
const fsp = fs.promises;
const fsp = require("fs/promises");
const os = require("os");
const path = require("path");
const timers = require("node:timers/promises");
Expand Down Expand Up @@ -1057,6 +1057,61 @@ describe("ESLint", () => {
await assert.rejects(async () => await eslint.lintFiles(["lib/cli.js"]), /Expected object with parse\(\) or parseForESLint\(\) method/u);
});

// https://github.com/eslint/eslint/issues/18407
it("should work in case when `fsp.readFile()` returns an object that is not an instance of Promise from this realm", async () => {

/**
* Promise wrapper
*/
class PromiseLike {
constructor(promise) {
this.promise = promise;
}
then(...args) {
return new PromiseLike(this.promise.then(...args));
}
catch(...args) {
return new PromiseLike(this.promise.catch(...args));
}
finally(...args) {
return new PromiseLike(this.promise.finally(...args));
}
}

const spy = sinon.spy(
(...args) => new PromiseLike(fsp.readFile(...args))
);

const { ESLint: LocalESLint } = proxyquire("../../../lib/eslint/eslint", {
"fs/promises": {
readFile: spy,
"@noCallThru": false // allows calling other methods of `fs/promises`
}
});

const testDir = "tests/fixtures/simple-valid-project";
const expectedLintedFiles = [
path.resolve(testDir, "foo.js"),
path.resolve(testDir, "src", "foobar.js")
];

eslint = new LocalESLint({
cwd: originalDir,
overrideConfigFile: path.resolve(testDir, "eslint.config.js")
});

const results = await eslint.lintFiles([`${testDir}/**/foo*.js`]);

assert.strictEqual(results.length, expectedLintedFiles.length);

expectedLintedFiles.forEach((file, index) => {
assert(spy.calledWith(file), `Spy was not called with ${file}`);
assert.strictEqual(results[index].filePath, file);
assert.strictEqual(results[index].messages.length, 0);
assert.strictEqual(results[index].suppressedMessages.length, 0);
});
});

describe("Invalid inputs", () => {

[
Expand Down Expand Up @@ -5513,13 +5568,10 @@ describe("ESLint", () => {
});

it("should call fs.writeFile() for each result with output", async () => {
const fakeFS = {
writeFile: sinon.spy(() => Promise.resolve())
};
const spy = fakeFS.writeFile;
const spy = sinon.spy(() => Promise.resolve());
const { ESLint: localESLint } = proxyquire("../../../lib/eslint/eslint", {
fs: {
promises: fakeFS
"fs/promises": {
writeFile: spy
}
});

Expand All @@ -5542,15 +5594,13 @@ describe("ESLint", () => {
});

it("should call fs.writeFile() for each result with output and not at all for a result without output", async () => {
const fakeFS = {
writeFile: sinon.spy(() => Promise.resolve())
};
const spy = fakeFS.writeFile;
const spy = sinon.spy(() => Promise.resolve());
const { ESLint: localESLint } = proxyquire("../../../lib/eslint/eslint", {
fs: {
promises: fakeFS
"fs/promises": {
writeFile: spy
}
});

const results = [
{
filePath: path.resolve("foo.js"),
Expand Down

0 comments on commit 37eba48

Please sign in to comment.