fs/lib/adapters/Memory.js

  1. import {getLogger} from "@ui5/logger";
  2. const log = getLogger("resources:adapters:Memory");
  3. import micromatch from "micromatch";
  4. import AbstractAdapter from "./AbstractAdapter.js";
  5. const ADAPTER_NAME = "Memory";
  6. /**
  7. * Virtual resource Adapter
  8. *
  9. * @public
  10. * @class
  11. * @alias @ui5/fs/adapters/Memory
  12. * @extends @ui5/fs/adapters/AbstractAdapter
  13. */
  14. class Memory extends AbstractAdapter {
  15. /**
  16. * The constructor.
  17. *
  18. * @public
  19. * @param {object} parameters Parameters
  20. * @param {string} parameters.virBasePath
  21. * Virtual base path. Must be absolute, POSIX-style, and must end with a slash
  22. * @param {string[]} [parameters.excludes] List of glob patterns to exclude
  23. * @param {@ui5/project/specifications/Project} [parameters.project] Project this adapter belongs to (if any)
  24. */
  25. constructor({virBasePath, project, excludes}) {
  26. super({virBasePath, project, excludes});
  27. this._virFiles = Object.create(null); // map full of files
  28. this._virDirs = Object.create(null); // map full of directories
  29. }
  30. /**
  31. * Matches and returns resources from a given map (either _virFiles or _virDirs).
  32. *
  33. * @private
  34. * @param {string[]} patterns
  35. * @param {object} resourceMap
  36. * @returns {Promise<module:@ui5/fs.Resource[]>}
  37. */
  38. async _matchPatterns(patterns, resourceMap) {
  39. const resourcePaths = Object.keys(resourceMap);
  40. const matchedPaths = micromatch(resourcePaths, patterns, {
  41. dot: true
  42. });
  43. return await Promise.all(matchedPaths.map((virPath) => {
  44. const resource = resourceMap[virPath];
  45. if (resource) {
  46. return this._cloneResource(resource);
  47. }
  48. }));
  49. }
  50. async _cloneResource(resource) {
  51. const clonedResource = await resource.clone();
  52. if (this._project) {
  53. clonedResource.setProject(this._project);
  54. }
  55. return clonedResource;
  56. }
  57. /**
  58. * Locate resources by glob.
  59. *
  60. * @private
  61. * @param {Array} patterns array of glob patterns
  62. * @param {object} [options={}] glob options
  63. * @param {boolean} [options.nodir=true] Do not match directories
  64. * @param {@ui5/fs/tracing.Trace} trace Trace instance
  65. * @returns {Promise<@ui5/fs/Resource[]>} Promise resolving to list of resources
  66. */
  67. async _runGlob(patterns, options = {nodir: true}, trace) {
  68. if (patterns[0] === "" && !options.nodir) { // Match virtual root directory
  69. return [
  70. this._createResource({
  71. project: this._project,
  72. statInfo: { // TODO: make closer to fs stat info
  73. isDirectory: function() {
  74. return true;
  75. }
  76. },
  77. sourceMetadata: {
  78. adapter: ADAPTER_NAME
  79. },
  80. path: this._virBasePath.slice(0, -1)
  81. })
  82. ];
  83. }
  84. let matchedResources = await this._matchPatterns(patterns, this._virFiles);
  85. if (!options.nodir) {
  86. const matchedDirs = await this._matchPatterns(patterns, this._virDirs);
  87. matchedResources = matchedResources.concat(matchedDirs);
  88. }
  89. return matchedResources;
  90. }
  91. /**
  92. * Locates resources by path.
  93. *
  94. * @private
  95. * @param {string} virPath Virtual path
  96. * @param {object} options Options
  97. * @param {@ui5/fs/tracing.Trace} trace Trace instance
  98. * @returns {Promise<@ui5/fs/Resource>} Promise resolving to a single resource
  99. */
  100. async _byPath(virPath, options, trace) {
  101. const relPath = this._resolveVirtualPathToBase(virPath);
  102. if (relPath === null) {
  103. return null;
  104. }
  105. trace.pathCall();
  106. const resource = this._virFiles[relPath];
  107. if (!resource || (options.nodir && resource.getStatInfo().isDirectory())) {
  108. return null;
  109. } else {
  110. return await this._cloneResource(resource);
  111. }
  112. }
  113. /**
  114. * Writes the content of a resource to a path.
  115. *
  116. * @private
  117. * @param {@ui5/fs/Resource} resource The Resource to write
  118. * @returns {Promise<undefined>} Promise resolving once data has been written
  119. */
  120. async _write(resource) {
  121. resource = this._migrateResource(resource);
  122. if (resource instanceof Promise) {
  123. // Only await if the migrate function returned a promise
  124. // Otherwise await would automatically create a Promise, causing unwanted overhead
  125. resource = await resource;
  126. }
  127. this._assignProjectToResource(resource);
  128. const relPath = this._resolveVirtualPathToBase(resource.getPath(), true);
  129. log.silly(`Writing to virtual path ${resource.getPath()}`);
  130. this._virFiles[relPath] = await resource.clone();
  131. // Add virtual directories for all path segments of the written resource
  132. // TODO: Add tests for all this
  133. const pathSegments = relPath.split("/");
  134. pathSegments.pop(); // Remove last segment representing the resource itself
  135. pathSegments.forEach((segment, i) => {
  136. if (i >= 1) {
  137. segment = pathSegments[i - 1] + "/" + segment;
  138. }
  139. pathSegments[i] = segment;
  140. });
  141. for (let i = pathSegments.length - 1; i >= 0; i--) {
  142. const segment = pathSegments[i];
  143. if (!this._virDirs[segment]) {
  144. this._virDirs[segment] = this._createResource({
  145. project: this._project,
  146. sourceMetadata: {
  147. adapter: ADAPTER_NAME
  148. },
  149. statInfo: { // TODO: make closer to fs stat info
  150. isDirectory: function() {
  151. return true;
  152. }
  153. },
  154. path: this._virBasePath + segment
  155. });
  156. }
  157. }
  158. }
  159. }
  160. export default Memory;