From 157edba05fdd63fa907bfffb4c1d59a07d7e2075 Mon Sep 17 00:00:00 2001
From: Tobias Koppers <tobias.koppers@googlemail.com>
Date: Tue, 29 Sep 2020 14:45:18 +0200
Subject: [PATCH] add lstat to filesystem interface support options argument in
 SyncAsyncFileSystem

---
 lib/CachedInputFileSystem.js        | 16 +++++++-
 lib/Resolver.js                     |  2 +
 lib/SyncAsyncFileSystemDecorator.js | 58 ++++++++++++++++++-----------
 3 files changed, 53 insertions(+), 23 deletions(-)

diff --git a/lib/CachedInputFileSystem.js b/lib/CachedInputFileSystem.js
index 64ba5d19..c06f13e5 100644
--- a/lib/CachedInputFileSystem.js
+++ b/lib/CachedInputFileSystem.js
@@ -141,8 +141,8 @@ class CacheBackend {
 		/** @type {number | undefined} */
 		this._nextDecay = undefined;
 
-		this.provide = this.provide.bind(this);
-		this.provideSync = this.provideSync.bind(this);
+		this.provide = provider ? this.provide.bind(this) : null;
+		this.provideSync = syncProvider ? this.provideSync.bind(this) : null;
 	}
 
 	provide(path, options, callback) {
@@ -382,6 +382,17 @@ module.exports = class CachedInputFileSystem {
 		const statSync = this._statBackend.provideSync;
 		this.statSync = /** @type {SyncFileSystem["statSync"]} */ (statSync);
 
+		this._lstatBackend = createBackend(
+			duration,
+			this.fileSystem.lstat,
+			this.fileSystem.lstatSync,
+			this.fileSystem
+		);
+		const lstat = this._lstatBackend.provide;
+		this.lstat = /** @type {FileSystem["lstat"]} */ (lstat);
+		const lstatSync = this._lstatBackend.provideSync;
+		this.lstatSync = /** @type {SyncFileSystem["lstatSync"]} */ (lstatSync);
+
 		this._readdirBackend = createBackend(
 			duration,
 			this.fileSystem.readdir,
@@ -451,6 +462,7 @@ module.exports = class CachedInputFileSystem {
 
 	purge(what) {
 		this._statBackend.purge(what);
+		this._lstatBackend.purge(what);
 		this._readdirBackend.purgeParent(what);
 		this._readFileBackend.purge(what);
 		this._readlinkBackend.purge(what);
diff --git a/lib/Resolver.js b/lib/Resolver.js
index d2a27b47..19a4d3fe 100644
--- a/lib/Resolver.js
+++ b/lib/Resolver.js
@@ -52,6 +52,7 @@ const {
  * @property {((function(string, FileSystemCallback<object>): void) & function(string, object, FileSystemCallback<object>): void)=} readJson
  * @property {(function(string, FileSystemCallback<Buffer | string>): void) & function(string, object, FileSystemCallback<Buffer | string>): void} readlink
  * @property {(function(string, FileSystemCallback<FileSystemStats>): void) & function(string, object, FileSystemCallback<Buffer | string>): void} stat
+ * @property {(function(string, FileSystemCallback<FileSystemStats>): void) & function(string, object, FileSystemCallback<Buffer | string>): void=} lstat
  */
 
 /**
@@ -61,6 +62,7 @@ const {
  * @property {(function(string, object=): object)=} readJsonSync
  * @property {function(string, object=): Buffer | string} readlinkSync
  * @property {function(string, object=): FileSystemStats} statSync
+ * @property {function(string, object=): FileSystemStats=} lstatSync
  */
 
 /**
diff --git a/lib/SyncAsyncFileSystemDecorator.js b/lib/SyncAsyncFileSystemDecorator.js
index 7bcdc97f..0dca1954 100644
--- a/lib/SyncAsyncFileSystemDecorator.js
+++ b/lib/SyncAsyncFileSystemDecorator.js
@@ -14,65 +14,81 @@
  */
 function SyncAsyncFileSystemDecorator(fs) {
 	this.fs = fs;
-	this.stat = (arg, callback) => {
+	this.stat = (arg, options, callback) => {
 		let result;
 		try {
-			result = fs.statSync(arg);
+			result = fs.statSync(arg, options);
 		} catch (e) {
-			return callback(e);
+			return (callback || options)(e);
 		}
-		callback(null, result);
+		(callback || options)(null, result);
 	};
-	this.statSync = arg => fs.statSync(arg);
+	this.statSync = (arg, options) => fs.statSync(arg, options);
 
-	this.readdir = (arg, callback) => {
+	this.lstat = undefined;
+	this.lstatSync = undefined;
+	const lstatSync = fs.lstatSync;
+	if (lstatSync) {
+		this.lstat = (arg, options, callback) => {
+			let result;
+			try {
+				result = lstatSync.call(fs, arg);
+			} catch (e) {
+				return (callback || options)(e);
+			}
+			(callback || options)(null, result);
+		};
+		this.lstatSync = (arg, options) => lstatSync.call(fs, arg, options);
+	}
+
+	this.readdir = (arg, options, callback) => {
 		let result;
 		try {
 			result = fs.readdirSync(arg);
 		} catch (e) {
-			return callback(e);
+			return (callback || options)(e);
 		}
-		callback(null, result);
+		(callback || options)(null, result);
 	};
-	this.readdirSync = arg => fs.readdirSync(arg);
+	this.readdirSync = (arg, options) => fs.readdirSync(arg, options);
 
-	this.readFile = (arg, callback) => {
+	this.readFile = (arg, options, callback) => {
 		let result;
 		try {
 			result = fs.readFileSync(arg);
 		} catch (e) {
-			return callback(e);
+			return (callback || options)(e);
 		}
-		callback(null, result);
+		(callback || options)(null, result);
 	};
-	this.readFileSync = arg => fs.readFileSync(arg);
+	this.readFileSync = (arg, options) => fs.readFileSync(arg, options);
 
-	this.readlink = (arg, callback) => {
+	this.readlink = (arg, options, callback) => {
 		let result;
 		try {
 			result = fs.readlinkSync(arg);
 		} catch (e) {
-			return callback(e);
+			return (callback || options)(e);
 		}
-		callback(null, result);
+		(callback || options)(null, result);
 	};
-	this.readlinkSync = arg => fs.readlinkSync(arg);
+	this.readlinkSync = (arg, options) => fs.readlinkSync(arg, options);
 
 	this.readJson = undefined;
 	this.readJsonSync = undefined;
 	const readJsonSync = fs.readJsonSync;
 	if (readJsonSync) {
-		this.readJson = (arg, callback) => {
+		this.readJson = (arg, options, callback) => {
 			let result;
 			try {
 				result = readJsonSync.call(fs, arg);
 			} catch (e) {
-				return callback(e);
+				return (callback || options)(e);
 			}
-			callback(null, result);
+			(callback || options)(null, result);
 		};
 
-		this.readJsonSync = arg => readJsonSync.call(fs, arg);
+		this.readJsonSync = (arg, options) => readJsonSync.call(fs, arg, options);
 	}
 }
 module.exports = SyncAsyncFileSystemDecorator;