Skip to content

Commit

Permalink
fix: support buffer/URL/number paths
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Mar 7, 2024
1 parent 2f51fb0 commit 074e44f
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 24 deletions.
68 changes: 44 additions & 24 deletions lib/CachedInputFileSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,16 +72,28 @@ class OperationMergerBackend {

this.provide = this._provider
? /**
* @param {string} path path
* @param {any} options options
* @param {function} callback callback
* @param {PathLike | PathOrFileDescriptor} path path
* @param {object | FileSystemCallback<any> | undefined} options options
* @param {FileSystemCallback<any>=} callback callback
* @returns {any} result
*/
(path, options, callback) => {
if (typeof options === "function") {
callback = options;
callback = /** @type {FileSystemCallback<any>} */ (options);
options = undefined;
}
if (
typeof path !== "string" &&
!Buffer.isBuffer(path) &&
!(path instanceof URL) &&
typeof path !== "number"
) {
/** @type {Function} */
(callback)(
new TypeError("path must be a string, Buffer, URL or number")
);
return;
}
if (options) {
return /** @type {Function} */ (this._provider).call(
this._providerContext,
Expand All @@ -90,10 +102,6 @@ class OperationMergerBackend {
callback
);
}
if (typeof path !== "string") {
callback(new TypeError("path must be a string"));
return;
}
let callbacks = this._activeAsyncOperations.get(path);
if (callbacks) {
callbacks.push(callback);
Expand All @@ -116,8 +124,8 @@ class OperationMergerBackend {
: null;
this.provideSync = this._syncProvider
? /**
* @param {string} path path
* @param {any} options options
* @param {PathLike | PathOrFileDescriptor} path path
* @param {object=} options options
* @returns {any} result
*/
(path, options) => {
Expand Down Expand Up @@ -213,10 +221,16 @@ class CacheBackend {
callback = options;
options = undefined;
}
if (typeof path !== "string") {
callback(new TypeError("path must be a string"));
if (
typeof path !== "string" &&
!Buffer.isBuffer(path) &&
!(path instanceof URL) &&
typeof path !== "number"
) {
callback(new TypeError("path must be a string, Buffer, URL or number"));
return;
}
const strPath = typeof path !== "string" ? path.toString() : path;
if (options) {
return /** @type {Function} */ (this._provider).call(
this._providerContext,
Expand All @@ -232,19 +246,19 @@ class CacheBackend {
}

// Check in cache
let cacheEntry = this._data.get(path);
let cacheEntry = this._data.get(strPath);
if (cacheEntry !== undefined) {
if (cacheEntry.err) return nextTick(callback, cacheEntry.err);
return nextTick(callback, null, cacheEntry.result);
}

// Check if there is already the same operation running
let callbacks = this._activeAsyncOperations.get(path);
let callbacks = this._activeAsyncOperations.get(strPath);
if (callbacks !== undefined) {
callbacks.push(callback);
return;
}
this._activeAsyncOperations.set(path, (callbacks = [callback]));
this._activeAsyncOperations.set(strPath, (callbacks = [callback]));

// Run the operation
/** @type {Function} */
Expand All @@ -256,8 +270,8 @@ class CacheBackend {
* @param {any} [result] result
*/
(err, result) => {
this._activeAsyncOperations.delete(path);
this._storeResult(path, err, result);
this._activeAsyncOperations.delete(strPath);
this._storeResult(strPath, err, result);

// Enter async mode if not yet done
this._enterAsyncMode();
Expand All @@ -277,9 +291,15 @@ class CacheBackend {
* @returns {any} result
*/
provideSync(path, options) {
if (typeof path !== "string") {
if (
typeof path !== "string" &&
!Buffer.isBuffer(path) &&
!(path instanceof URL) &&
typeof path !== "number"
) {
throw new TypeError("path must be a string");
}
const strPath = typeof path !== "string" ? path.toString() : path;
if (options) {
return /** @type {Function} */ (this._syncProvider).call(
this._providerContext,
Expand All @@ -294,16 +314,16 @@ class CacheBackend {
}

// Check in cache
let cacheEntry = this._data.get(path);
let cacheEntry = this._data.get(strPath);
if (cacheEntry !== undefined) {
if (cacheEntry.err) throw cacheEntry.err;
return cacheEntry.result;
}

// Get all active async operations
// This sync operation will also complete them
const callbacks = this._activeAsyncOperations.get(path);
this._activeAsyncOperations.delete(path);
const callbacks = this._activeAsyncOperations.get(strPath);
this._activeAsyncOperations.delete(strPath);

// Run the operation
// When in idle mode, we will enter sync mode
Expand All @@ -314,14 +334,14 @@ class CacheBackend {
path
);
} catch (err) {
this._storeResult(path, /** @type {Error} */ (err), undefined);
this._storeResult(strPath, /** @type {Error} */ (err), undefined);
this._enterSyncModeWhenIdle();
if (callbacks) {
runCallbacks(callbacks, /** @type {Error} */ (err), undefined);
}
throw err;
}
this._storeResult(path, null, result);
this._storeResult(strPath, null, result);
this._enterSyncModeWhenIdle();
if (callbacks) {
runCallbacks(callbacks, null, result);
Expand Down Expand Up @@ -368,7 +388,7 @@ class CacheBackend {
}

/**
* @param {string|string[]|Set<string>} [what] what to purge
* @param {string | string[] | Set<string>} [what] what to purge
*/
purgeParent(what) {
if (!what) {
Expand Down
118 changes: 118 additions & 0 deletions test/CachedInputFileSystem.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const { CachedInputFileSystem } = require("../");
const path = require("path");
const url = require("url");

describe("CachedInputFileSystem OperationMergerBackend ('stat' and 'statSync')", () => {
let fs;
Expand Down Expand Up @@ -453,3 +455,119 @@ describe("CachedInputFileSystem CacheBackend", () => {
next();
});
});

describe("CachedInputFileSystem CacheBackend and Node.JS filesystem", () => {
let fs;

beforeEach(() => {
fs = new CachedInputFileSystem(require("fs"), 1);
});

const file = path.resolve(__dirname, "./fixtures/abc.txt");

it("should work with string async", function (done) {
fs.readFile(file, (err, r) => {
if (err) {
done(err);
return;
}
expect(r.toString()).toEqual("abc");
done();
});
});

it("should work with string sync", function () {
const r = fs.readFileSync(file);
expect(r.toString()).toEqual("abc");
});

it("should work with Buffer async", function (done) {
fs.readFile(Buffer.from(file), (err, r) => {
if (err) {
done(err);
return;
}
expect(r.toString()).toEqual("abc");
done();
});
});

it("should work with Buffer sync", function () {
const r = fs.readFileSync(Buffer.from(file));
expect(r.toString()).toEqual("abc");
});

it("should work with URL async", function (done) {
fs.readFile(url.pathToFileURL(file), (err, r) => {
if (err) {
done(err);
return;
}
expect(r.toString()).toEqual("abc");
done();
});
});

it("should work with URL sync", function () {
const r = fs.readFileSync(url.pathToFileURL(file));
expect(r.toString()).toEqual("abc");
});
});

describe("CachedInputFileSystem OperationMergerBackend and Node.JS filesystem", () => {
let fs;

beforeEach(() => {
fs = new CachedInputFileSystem(require("fs"), 0);
});

const file = path.resolve(__dirname, "./fixtures/abc.txt");

it("should work with string async", function (done) {
fs.readFile(file, (err, r) => {
if (err) {
done(err);
return;
}
expect(r.toString()).toEqual("abc");
done();
});
});

it("should work with string sync", function () {
const r = fs.readFileSync(file);
expect(r.toString()).toEqual("abc");
});

it("should work with Buffer async", function (done) {
fs.readFile(Buffer.from(file), (err, r) => {
if (err) {
done(err);
return;
}
expect(r.toString()).toEqual("abc");
done();
});
});

it("should work with Buffer sync", function () {
const r = fs.readFileSync(Buffer.from(file));
expect(r.toString()).toEqual("abc");
});

it("should work with URL async", function (done) {
fs.readFile(url.pathToFileURL(file), (err, r) => {
if (err) {
done(err);
return;
}
expect(r.toString()).toEqual("abc");
done();
});
});

it("should work with URL sync", function () {
const r = fs.readFileSync(url.pathToFileURL(file));
expect(r.toString()).toEqual("abc");
});
});

0 comments on commit 074e44f

Please sign in to comment.