@ui5/fs
Version:
UI5 Build and Development Tooling - File System Abstraction
291 lines (264 loc) • 6.9 kB
JavaScript
const stream = require("stream");
const clone = require("clone");
const path = require("path");
const fnTrue = () => true;
const fnFalse = () => false;
/**
* Resource
*
* @public
* @memberof module:@ui5/fs
*/
class Resource {
/**
* The constructor.
*
* @public
* @param {Object} parameters Parameters
* @param {string} parameters.path Virtual path
* @param {Object} [parameters.statInfo] File stat information
* @param {Buffer} [parameters.buffer] Content of this resources as a Buffer instance
* (cannot be used in conjunction with parameters string, stream or createStream)
* @param {string} [parameters.string] Content of this resources as a string
* (cannot be used in conjunction with parameters buffer, stream or createStream)
* @param {Stream} [parameters.stream] Readable stream of the content of this resource
* (cannot be used in conjunction with parameters buffer, string or createStream)
* @param {Function} [parameters.createStream] Function callback that returns a readable stream of the content
* of this resource (cannot be used in conjunction with parameters buffer, string or stream)
*/
constructor({path, statInfo, buffer, string, createStream, stream, project}) {
if (!path) {
throw new Error("Cannot create Resource: path parameter missing");
}
if (buffer && createStream || buffer && string || string && createStream || buffer && stream ||
string && stream || createStream && stream) {
throw new Error("Cannot create Resource: Please set only one content parameter. " +
"Buffer, string, stream or createStream");
}
this._path = path;
this._name = this._getNameFromPath(path);
this._project = project; // Experimental, internal parameter
this._statInfo = statInfo || { // TODO
isFile: fnTrue,
isDirectory: fnFalse,
isBlockDevice: fnFalse,
isCharacterDevice: fnFalse,
isSymbolicLink: fnFalse,
isFIFO: fnFalse,
isSocket: fnFalse,
atimeMs: new Date().getTime(),
mtimeMs: new Date().getTime(),
ctimeMs: new Date().getTime(),
birthtimeMs: new Date().getTime(),
atime: new Date(),
mtime: new Date(),
ctime: new Date(),
birthtime: new Date()
};
this._createStream = createStream || null;
this._stream = stream || null;
this._buffer = buffer || null;
if (string) {
this._buffer = Buffer.from(string, "utf8");
}
// Tracing:
this._collections = [];
}
/**
* Gets a buffer with the resource content.
*
* @public
* @returns {Promise<Buffer>} A Promise resolving with a buffer of the resource content.
*/
getBuffer() {
return new Promise((resolve, reject) => {
if (this._buffer) {
resolve(this._buffer);
} else if (this._createStream || this._stream) {
resolve(this._getBufferFromStream());
} else {
reject(new Error(`Resource ${this._path} has no content`));
}
});
}
/**
* Sets a Buffer as content.
*
* @public
* @param {Buffer} buffer A buffer instance
*/
setBuffer(buffer) {
this._createStream = null;
// if (this._stream) { // TODO this may cause strange issues
// this._stream.destroy();
// }
this._stream = null;
this._buffer = buffer;
}
/**
* Gets a string with the resource content.
*
* @public
* @returns {Promise<string>} A Promise resolving with a string of the resource content.
*/
getString() {
return this.getBuffer().then((buffer) => buffer.toString());
}
/**
* Sets a String as content
*
* @public
* @param {string} string A string
*/
setString(string) {
this.setBuffer(Buffer.from(string, "utf8"));
}
/**
* Gets a readable stream for the resource content.
*
* @public
* @returns {stream.Readable} A readable stream for the resource content.
*/
getStream() {
if (this._buffer) {
const bufferStream = new stream.PassThrough();
bufferStream.end(this._buffer);
return bufferStream;
} else if (this._createStream || this._stream) {
return this._getStream();
} else {
throw new Error(`Resource ${this._path} has no content`);
}
}
/**
* Sets a readable stream as content.
*
* @public
* @param {stream.Readable} stream readable stream
*/
setStream(stream) {
this._buffer = null;
this._createStream = null;
// if (this._stream) { // TODO this may cause strange issues
// this._stream.destroy();
// }
this._stream = stream;
}
/**
* Gets the resources path
*
* @public
* @returns {string} (Virtual) path of the resource
*/
getPath() {
return this._path;
}
/**
* Sets the resources path
*
* @public
* @param {string} path (Virtual) path of the resource
*/
setPath(path) {
this._path = path;
this._name = this._getNameFromPath(path);
}
/**
* Gets the resources stat info.
*
* @public
* @returns {fs.Stats} An object representing an fs.Stats instance
*/
getStatInfo() {
return this._statInfo;
}
_getNameFromPath(virPath) {
return path.posix.basename(virPath);
}
/**
* Adds a resource collection name that was involved in locating this resource.
*
* @param {string} name Resource collection name
*/
pushCollection(name) {
this._collections.push(name);
}
/**
* Returns a clone of the resource.
*
* @public
* @returns {Promise<module:@ui5/fs.Resource>} A promise resolving the resource.
*/
clone() {
const options = {
path: this._path,
statInfo: clone(this._statInfo)
};
const addContentOption = () => {
if (this._stream) {
return this._getBufferFromStream().then(function(buffer) {
options.buffer = buffer;
});
} else {
if (this._createStream) {
options.createStream = this._createStream;
} else if (this._buffer) {
options.buffer = this._buffer;
}
return Promise.resolve();
}
};
return addContentOption().then(() => {
return new Resource(options);
});
}
/**
* Tracing: Get tree for printing out trace
*
* @returns {Object}
*/
getPathTree() {
const tree = {};
let pointer = tree[this._path] = {};
for (let i = this._collections.length - 1; i >= 0; i--) {
pointer = pointer[this._collections[i]] = {};
}
return tree;
}
/**
* Returns the content as stream.
*
* @private
* @returns {Function} The stream
*/
_getStream() {
if (this._createStream) {
return this._createStream();
}
return this._stream;
}
/**
* Converts the buffer into a stream.
*
* @private
* @returns {Promise<Buffer>} Promise resolving with buffer.
*/
_getBufferFromStream() {
return new Promise((resolve, reject) => {
const contentStream = this._getStream();
const buffers = [];
contentStream.on("data", (data) => {
buffers.push(data);
});
contentStream.on("error", (err) => {
reject(err);
});
contentStream.on("end", () => {
const buffer = Buffer.concat(buffers);
this.setBuffer(buffer);
resolve(buffer);
});
});
}
}
module.exports = Resource;