Skip to content

Commit 074e44f

Browse files
fix: support buffer/URL/number paths
1 parent 2f51fb0 commit 074e44f

File tree

2 files changed

+162
-24
lines changed

2 files changed

+162
-24
lines changed

lib/CachedInputFileSystem.js

+44-24
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,28 @@ class OperationMergerBackend {
7272

7373
this.provide = this._provider
7474
? /**
75-
* @param {string} path path
76-
* @param {any} options options
77-
* @param {function} callback callback
75+
* @param {PathLike | PathOrFileDescriptor} path path
76+
* @param {object | FileSystemCallback<any> | undefined} options options
77+
* @param {FileSystemCallback<any>=} callback callback
7878
* @returns {any} result
7979
*/
8080
(path, options, callback) => {
8181
if (typeof options === "function") {
82-
callback = options;
82+
callback = /** @type {FileSystemCallback<any>} */ (options);
8383
options = undefined;
8484
}
85+
if (
86+
typeof path !== "string" &&
87+
!Buffer.isBuffer(path) &&
88+
!(path instanceof URL) &&
89+
typeof path !== "number"
90+
) {
91+
/** @type {Function} */
92+
(callback)(
93+
new TypeError("path must be a string, Buffer, URL or number")
94+
);
95+
return;
96+
}
8597
if (options) {
8698
return /** @type {Function} */ (this._provider).call(
8799
this._providerContext,
@@ -90,10 +102,6 @@ class OperationMergerBackend {
90102
callback
91103
);
92104
}
93-
if (typeof path !== "string") {
94-
callback(new TypeError("path must be a string"));
95-
return;
96-
}
97105
let callbacks = this._activeAsyncOperations.get(path);
98106
if (callbacks) {
99107
callbacks.push(callback);
@@ -116,8 +124,8 @@ class OperationMergerBackend {
116124
: null;
117125
this.provideSync = this._syncProvider
118126
? /**
119-
* @param {string} path path
120-
* @param {any} options options
127+
* @param {PathLike | PathOrFileDescriptor} path path
128+
* @param {object=} options options
121129
* @returns {any} result
122130
*/
123131
(path, options) => {
@@ -213,10 +221,16 @@ class CacheBackend {
213221
callback = options;
214222
options = undefined;
215223
}
216-
if (typeof path !== "string") {
217-
callback(new TypeError("path must be a string"));
224+
if (
225+
typeof path !== "string" &&
226+
!Buffer.isBuffer(path) &&
227+
!(path instanceof URL) &&
228+
typeof path !== "number"
229+
) {
230+
callback(new TypeError("path must be a string, Buffer, URL or number"));
218231
return;
219232
}
233+
const strPath = typeof path !== "string" ? path.toString() : path;
220234
if (options) {
221235
return /** @type {Function} */ (this._provider).call(
222236
this._providerContext,
@@ -232,19 +246,19 @@ class CacheBackend {
232246
}
233247

234248
// Check in cache
235-
let cacheEntry = this._data.get(path);
249+
let cacheEntry = this._data.get(strPath);
236250
if (cacheEntry !== undefined) {
237251
if (cacheEntry.err) return nextTick(callback, cacheEntry.err);
238252
return nextTick(callback, null, cacheEntry.result);
239253
}
240254

241255
// Check if there is already the same operation running
242-
let callbacks = this._activeAsyncOperations.get(path);
256+
let callbacks = this._activeAsyncOperations.get(strPath);
243257
if (callbacks !== undefined) {
244258
callbacks.push(callback);
245259
return;
246260
}
247-
this._activeAsyncOperations.set(path, (callbacks = [callback]));
261+
this._activeAsyncOperations.set(strPath, (callbacks = [callback]));
248262

249263
// Run the operation
250264
/** @type {Function} */
@@ -256,8 +270,8 @@ class CacheBackend {
256270
* @param {any} [result] result
257271
*/
258272
(err, result) => {
259-
this._activeAsyncOperations.delete(path);
260-
this._storeResult(path, err, result);
273+
this._activeAsyncOperations.delete(strPath);
274+
this._storeResult(strPath, err, result);
261275

262276
// Enter async mode if not yet done
263277
this._enterAsyncMode();
@@ -277,9 +291,15 @@ class CacheBackend {
277291
* @returns {any} result
278292
*/
279293
provideSync(path, options) {
280-
if (typeof path !== "string") {
294+
if (
295+
typeof path !== "string" &&
296+
!Buffer.isBuffer(path) &&
297+
!(path instanceof URL) &&
298+
typeof path !== "number"
299+
) {
281300
throw new TypeError("path must be a string");
282301
}
302+
const strPath = typeof path !== "string" ? path.toString() : path;
283303
if (options) {
284304
return /** @type {Function} */ (this._syncProvider).call(
285305
this._providerContext,
@@ -294,16 +314,16 @@ class CacheBackend {
294314
}
295315

296316
// Check in cache
297-
let cacheEntry = this._data.get(path);
317+
let cacheEntry = this._data.get(strPath);
298318
if (cacheEntry !== undefined) {
299319
if (cacheEntry.err) throw cacheEntry.err;
300320
return cacheEntry.result;
301321
}
302322

303323
// Get all active async operations
304324
// This sync operation will also complete them
305-
const callbacks = this._activeAsyncOperations.get(path);
306-
this._activeAsyncOperations.delete(path);
325+
const callbacks = this._activeAsyncOperations.get(strPath);
326+
this._activeAsyncOperations.delete(strPath);
307327

308328
// Run the operation
309329
// When in idle mode, we will enter sync mode
@@ -314,14 +334,14 @@ class CacheBackend {
314334
path
315335
);
316336
} catch (err) {
317-
this._storeResult(path, /** @type {Error} */ (err), undefined);
337+
this._storeResult(strPath, /** @type {Error} */ (err), undefined);
318338
this._enterSyncModeWhenIdle();
319339
if (callbacks) {
320340
runCallbacks(callbacks, /** @type {Error} */ (err), undefined);
321341
}
322342
throw err;
323343
}
324-
this._storeResult(path, null, result);
344+
this._storeResult(strPath, null, result);
325345
this._enterSyncModeWhenIdle();
326346
if (callbacks) {
327347
runCallbacks(callbacks, null, result);
@@ -368,7 +388,7 @@ class CacheBackend {
368388
}
369389

370390
/**
371-
* @param {string|string[]|Set<string>} [what] what to purge
391+
* @param {string | string[] | Set<string>} [what] what to purge
372392
*/
373393
purgeParent(what) {
374394
if (!what) {

test/CachedInputFileSystem.test.js

+118
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
const { CachedInputFileSystem } = require("../");
2+
const path = require("path");
3+
const url = require("url");
24

35
describe("CachedInputFileSystem OperationMergerBackend ('stat' and 'statSync')", () => {
46
let fs;
@@ -453,3 +455,119 @@ describe("CachedInputFileSystem CacheBackend", () => {
453455
next();
454456
});
455457
});
458+
459+
describe("CachedInputFileSystem CacheBackend and Node.JS filesystem", () => {
460+
let fs;
461+
462+
beforeEach(() => {
463+
fs = new CachedInputFileSystem(require("fs"), 1);
464+
});
465+
466+
const file = path.resolve(__dirname, "./fixtures/abc.txt");
467+
468+
it("should work with string async", function (done) {
469+
fs.readFile(file, (err, r) => {
470+
if (err) {
471+
done(err);
472+
return;
473+
}
474+
expect(r.toString()).toEqual("abc");
475+
done();
476+
});
477+
});
478+
479+
it("should work with string sync", function () {
480+
const r = fs.readFileSync(file);
481+
expect(r.toString()).toEqual("abc");
482+
});
483+
484+
it("should work with Buffer async", function (done) {
485+
fs.readFile(Buffer.from(file), (err, r) => {
486+
if (err) {
487+
done(err);
488+
return;
489+
}
490+
expect(r.toString()).toEqual("abc");
491+
done();
492+
});
493+
});
494+
495+
it("should work with Buffer sync", function () {
496+
const r = fs.readFileSync(Buffer.from(file));
497+
expect(r.toString()).toEqual("abc");
498+
});
499+
500+
it("should work with URL async", function (done) {
501+
fs.readFile(url.pathToFileURL(file), (err, r) => {
502+
if (err) {
503+
done(err);
504+
return;
505+
}
506+
expect(r.toString()).toEqual("abc");
507+
done();
508+
});
509+
});
510+
511+
it("should work with URL sync", function () {
512+
const r = fs.readFileSync(url.pathToFileURL(file));
513+
expect(r.toString()).toEqual("abc");
514+
});
515+
});
516+
517+
describe("CachedInputFileSystem OperationMergerBackend and Node.JS filesystem", () => {
518+
let fs;
519+
520+
beforeEach(() => {
521+
fs = new CachedInputFileSystem(require("fs"), 0);
522+
});
523+
524+
const file = path.resolve(__dirname, "./fixtures/abc.txt");
525+
526+
it("should work with string async", function (done) {
527+
fs.readFile(file, (err, r) => {
528+
if (err) {
529+
done(err);
530+
return;
531+
}
532+
expect(r.toString()).toEqual("abc");
533+
done();
534+
});
535+
});
536+
537+
it("should work with string sync", function () {
538+
const r = fs.readFileSync(file);
539+
expect(r.toString()).toEqual("abc");
540+
});
541+
542+
it("should work with Buffer async", function (done) {
543+
fs.readFile(Buffer.from(file), (err, r) => {
544+
if (err) {
545+
done(err);
546+
return;
547+
}
548+
expect(r.toString()).toEqual("abc");
549+
done();
550+
});
551+
});
552+
553+
it("should work with Buffer sync", function () {
554+
const r = fs.readFileSync(Buffer.from(file));
555+
expect(r.toString()).toEqual("abc");
556+
});
557+
558+
it("should work with URL async", function (done) {
559+
fs.readFile(url.pathToFileURL(file), (err, r) => {
560+
if (err) {
561+
done(err);
562+
return;
563+
}
564+
expect(r.toString()).toEqual("abc");
565+
done();
566+
});
567+
});
568+
569+
it("should work with URL sync", function () {
570+
const r = fs.readFileSync(url.pathToFileURL(file));
571+
expect(r.toString()).toEqual("abc");
572+
});
573+
});

0 commit comments

Comments
 (0)