256 lines
7.5 KiB
JavaScript
256 lines
7.5 KiB
JavaScript
"use strict";
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const path_1 = __importDefault(require("path"));
|
|
const immutable_1 = __importDefault(require("immutable"));
|
|
const stream_1 = __importDefault(require("stream"));
|
|
const file_1 = __importDefault(require("./file"));
|
|
const promise_1 = __importDefault(require("../utils/promise"));
|
|
const error_1 = __importDefault(require("../utils/error"));
|
|
const path_2 = __importDefault(require("../utils/path"));
|
|
class FS extends immutable_1.default.Record({
|
|
root: String(),
|
|
fsExists: Function(),
|
|
fsReadFile: Function(),
|
|
fsStatFile: Function(),
|
|
fsReadDir: Function(),
|
|
fsLoadObject: null,
|
|
fsReadAsStream: null
|
|
}) {
|
|
/**
|
|
Return path to the root
|
|
|
|
@return {string}
|
|
*/
|
|
getRoot() {
|
|
return this.get("root");
|
|
}
|
|
/**
|
|
Verify that a file is in the fs scope
|
|
|
|
@param {string} filename
|
|
@return {boolean}
|
|
*/
|
|
isInScope(filename) {
|
|
const rootPath = this.getRoot();
|
|
filename = path_1.default.join(rootPath, filename);
|
|
return path_2.default.isInRoot(rootPath, filename);
|
|
}
|
|
/**
|
|
Resolve a file in this FS
|
|
@return {string}
|
|
*/
|
|
resolve(...args) {
|
|
const rootPath = this.getRoot();
|
|
let filename = path_1.default.join.apply(path_1.default, [rootPath].concat(args));
|
|
filename = path_1.default.normalize(filename);
|
|
if (!this.isInScope(filename)) {
|
|
throw error_1.default.FileOutOfScopeError({
|
|
filename: filename,
|
|
root: rootPath
|
|
});
|
|
}
|
|
return filename;
|
|
}
|
|
/**
|
|
Check if a file exists, run a Promise(true) if that's the case, Promise(false) otherwise
|
|
|
|
@param {string} filename
|
|
@return {Promise<Boolean>}
|
|
*/
|
|
exists(filename) {
|
|
const that = this;
|
|
return (0, promise_1.default)().then(() => {
|
|
filename = that.resolve(filename);
|
|
const exists = that.get("fsExists");
|
|
return exists(filename);
|
|
});
|
|
}
|
|
/**
|
|
Read a file and returns a promise with the content as a buffer
|
|
|
|
@param {string} filename
|
|
@return {Promise<Buffer>}
|
|
*/
|
|
read(filename) {
|
|
const that = this;
|
|
return (0, promise_1.default)().then(() => {
|
|
filename = that.resolve(filename);
|
|
const read = that.get("fsReadFile");
|
|
return read(filename);
|
|
});
|
|
}
|
|
/**
|
|
Read a file as a string (utf-8)
|
|
@return {Promise<String>}
|
|
*/
|
|
readAsString(filename, encoding = "utf8") {
|
|
return this.read(filename).then((buf) => {
|
|
return buf.toString(encoding);
|
|
});
|
|
}
|
|
/**
|
|
Read file as a stream
|
|
|
|
@param {string} filename
|
|
@return {Promise<Stream>}
|
|
*/
|
|
readAsStream(filename) {
|
|
const that = this;
|
|
const filepath = that.resolve(filename);
|
|
const fsReadAsStream = this.get("fsReadAsStream");
|
|
if (fsReadAsStream) {
|
|
return (0, promise_1.default)(fsReadAsStream(filepath));
|
|
}
|
|
return this.read(filename).then((buf) => {
|
|
const bufferStream = new stream_1.default.PassThrough();
|
|
bufferStream.end(buf);
|
|
return bufferStream;
|
|
});
|
|
}
|
|
/**
|
|
Read stat infos about a file
|
|
|
|
@param {string} filename
|
|
@return {Promise<File>}
|
|
*/
|
|
statFile(filename) {
|
|
const that = this;
|
|
return (0, promise_1.default)()
|
|
.then(() => {
|
|
const filepath = that.resolve(filename);
|
|
const stat = that.get("fsStatFile");
|
|
return stat(filepath);
|
|
})
|
|
.then((stat) => {
|
|
return file_1.default.createFromStat(filename, stat);
|
|
});
|
|
}
|
|
/**
|
|
List files/directories in a directory.
|
|
Directories ends with '/'
|
|
|
|
@param {string} dirname
|
|
@return {Promise<List<String>>}
|
|
*/
|
|
readDir(dirname) {
|
|
const that = this;
|
|
return (0, promise_1.default)()
|
|
.then(() => {
|
|
const dirpath = that.resolve(dirname);
|
|
const readDir = that.get("fsReadDir");
|
|
return readDir(dirpath);
|
|
})
|
|
.then((files) => {
|
|
return immutable_1.default.List(files);
|
|
});
|
|
}
|
|
/**
|
|
List only files in a diretcory
|
|
Directories ends with '/'
|
|
|
|
@param {string} dirname
|
|
@return {Promise<List<String>>}
|
|
*/
|
|
listFiles(dirname) {
|
|
return this.readDir(dirname).then((files) => {
|
|
return files.filterNot(pathIsFolder);
|
|
});
|
|
}
|
|
/**
|
|
List all files in a directory
|
|
|
|
@param {string} dirName
|
|
@param {Function(dirName)} filterFn: call it for each file/directory to test if it should stop iterating
|
|
@return {Promise<List<String>>}
|
|
*/
|
|
listAllFiles(dirName, filterFn) {
|
|
const that = this;
|
|
dirName = dirName || ".";
|
|
return this.readDir(dirName).then((files) => {
|
|
return promise_1.default.reduce(files, (out, file) => {
|
|
const isDirectory = pathIsFolder(file);
|
|
const newDirName = path_1.default.join(dirName, file);
|
|
if (filterFn && filterFn(newDirName) === false) {
|
|
return out;
|
|
}
|
|
if (!isDirectory) {
|
|
return out.push(newDirName);
|
|
}
|
|
return that.listAllFiles(newDirName, filterFn).then((inner) => {
|
|
return out.concat(inner);
|
|
});
|
|
}, immutable_1.default.List());
|
|
});
|
|
}
|
|
/**
|
|
Find a file in a folder (case insensitive)
|
|
Return the found filename
|
|
|
|
@param {string} dirname
|
|
@param {string} filename
|
|
@return {Promise<String>}
|
|
*/
|
|
findFile(dirname, filename) {
|
|
return this.listFiles(dirname).then((files) => {
|
|
return files.find((file) => {
|
|
return file.toLowerCase() == filename.toLowerCase();
|
|
});
|
|
});
|
|
}
|
|
/**
|
|
Load a JSON file
|
|
By default, fs only supports JSON
|
|
|
|
@param {string} filename
|
|
@return {Promise<Object>}
|
|
*/
|
|
loadAsObject(filename) {
|
|
const that = this;
|
|
const fsLoadObject = this.get("fsLoadObject");
|
|
return this.exists(filename).then((exists) => {
|
|
if (!exists) {
|
|
const err = new Error("Module doesn't exist");
|
|
// @ts-expect-error ts-migrate(2339) FIXME: Property 'code' does not exist on type 'Error'.
|
|
err.code = "MODULE_NOT_FOUND";
|
|
throw err;
|
|
}
|
|
if (fsLoadObject) {
|
|
return fsLoadObject(that.resolve(filename));
|
|
}
|
|
else {
|
|
return that.readAsString(filename).then((str) => {
|
|
return JSON.parse(str);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
Create a FS instance
|
|
|
|
@param {Object} def
|
|
@return {FS}
|
|
*/
|
|
static create(def) {
|
|
return new FS(def);
|
|
}
|
|
/**
|
|
Create a new FS instance with a reduced scope
|
|
|
|
@param {FS} fs
|
|
@param {string} scope
|
|
@return {FS}
|
|
*/
|
|
static reduceScope(fs, scope) {
|
|
return fs.set("root", path_1.default.join(fs.getRoot(), scope));
|
|
}
|
|
}
|
|
// .readdir return files/folder as a list of string, folder ending with '/'
|
|
function pathIsFolder(filename) {
|
|
const lastChar = filename[filename.length - 1];
|
|
return lastChar == "/" || lastChar == "\\";
|
|
}
|
|
exports.default = FS;
|