const log = require("@ui5/logger").getLogger("resources:adapters:Memory");
const micromatch = require("micromatch");
const Resource = require("../Resource");
const AbstractAdapter = require("./AbstractAdapter");
/**
* Virtual resource Adapter
*
* @public
* @memberof module:@ui5/fs.adapters
* @augments module:@ui5/fs.adapters.AbstractAdapter
*/
class Memory extends AbstractAdapter {
/**
* The constructor.
*
* @public
* @param {object} parameters Parameters
* @param {string} parameters.virBasePath Virtual base path
* @param {string[]} [parameters.excludes] List of glob patterns to exclude
* @param {object} [parameters.project] Experimental, internal parameter. Do not use
*/
constructor({virBasePath, project, excludes}) {
super({virBasePath, project, excludes});
this._virFiles = {}; // map full of files
this._virDirs = {}; // map full of directories
}
/**
* Locate resources by glob.
*
* @private
* @param {Array} patterns array of glob patterns
* @param {object} [options={}] glob options
* @param {boolean} [options.nodir=true] Do not match directories
* @param {module:@ui5/fs.tracing.Trace} trace Trace instance
* @returns {Promise<module:@ui5/fs.Resource[]>} Promise resolving to list of resources
*/
async _runGlob(patterns, options = {nodir: true}, trace) {
if (patterns[0] === "" && !options.nodir) { // Match virtual root directory
return [
new Resource({
project: this.project,
statInfo: { // TODO: make closer to fs stat info
isDirectory: function() {
return true;
}
},
path: this._virBasePath.slice(0, -1)
})
];
}
const filePaths = Object.keys(this._virFiles);
const matchedFilePaths = micromatch(filePaths, patterns, {
dot: true
});
let matchedResources = matchedFilePaths.map((virPath) => {
return this._virFiles[virPath];
});
if (!options.nodir) {
const dirPaths = Object.keys(this._virDirs);
const matchedDirs = micromatch(dirPaths, patterns, {
dot: true
});
matchedResources = matchedResources.concat(matchedDirs.map((virPath) => {
return this._virDirs[virPath];
}));
}
return matchedResources;
}
/**
* Locates resources by path.
*
* @private
* @param {string} virPath Virtual path
* @param {object} options Options
* @param {module:@ui5/fs.tracing.Trace} trace Trace instance
* @returns {Promise<module:@ui5/fs.Resource>} Promise resolving to a single resource
*/
_byPath(virPath, options, trace) {
if (this.isPathExcluded(virPath)) {
return Promise.resolve(null);
}
return new Promise((resolve, reject) => {
if (!virPath.startsWith(this._virBasePath) && virPath !== this._virBaseDir) {
// Neither starts with basePath, nor equals baseDirectory
resolve(null);
return;
}
const relPath = virPath.substr(this._virBasePath.length);
trace.pathCall();
const resource = this._virFiles[relPath];
if (!resource || (options.nodir && resource.getStatInfo().isDirectory())) {
resolve(null);
} else {
resolve(resource);
}
});
}
/**
* Writes the content of a resource to a path.
*
* @private
* @param {module:@ui5/fs.Resource} resource The Resource to write
* @returns {Promise<undefined>} Promise resolving once data has been written
*/
_write(resource) {
return new Promise((resolve, reject) => {
const relPath = resource.getPath().substr(this._virBasePath.length);
log.verbose("Writing to virtual path %s", resource.getPath());
this._virFiles[relPath] = resource;
// Add virtual directories for all path segments of the written resource
// TODO: Add tests for all this
const pathSegments = relPath.split("/");
pathSegments.pop(); // Remove last segment representing the resource itself
pathSegments.forEach((segment, i) => {
if (i >= 1) {
segment = pathSegments[i - 1] + "/" + segment;
}
pathSegments[i] = segment;
});
for (let i = pathSegments.length - 1; i >= 0; i--) {
const segment = pathSegments[i];
if (!this._virDirs[segment]) {
this._virDirs[segment] = new Resource({
project: this.project,
statInfo: { // TODO: make closer to fs stat info
isDirectory: function() {
return true;
}
},
path: this._virBasePath + segment
});
}
}
resolve();
});
}
}
module.exports = Memory;