DescriptionFileUtils.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const forEachBail = require("./forEachBail");
  7. /** @typedef {import("./Resolver")} Resolver */
  8. /** @typedef {import("./Resolver").JsonObject} JsonObject */
  9. /** @typedef {import("./Resolver").JsonValue} JsonValue */
  10. /** @typedef {import("./Resolver").ResolveContext} ResolveContext */
  11. /** @typedef {import("./Resolver").ResolveRequest} ResolveRequest */
  12. /**
  13. * @typedef {Object} DescriptionFileInfo
  14. * @property {JsonObject=} content
  15. * @property {string} path
  16. * @property {string} directory
  17. */
  18. /**
  19. * @callback ErrorFirstCallback
  20. * @param {Error|null=} error
  21. * @param {DescriptionFileInfo=} result
  22. */
  23. /**
  24. * @typedef {Object} Result
  25. * @property {string} path path to description file
  26. * @property {string} directory directory of description file
  27. * @property {JsonObject} content content of description file
  28. */
  29. /**
  30. * @param {Resolver} resolver resolver
  31. * @param {string} directory directory
  32. * @param {string[]} filenames filenames
  33. * @param {DescriptionFileInfo|undefined} oldInfo oldInfo
  34. * @param {ResolveContext} resolveContext resolveContext
  35. * @param {ErrorFirstCallback} callback callback
  36. */
  37. function loadDescriptionFile(
  38. resolver,
  39. directory,
  40. filenames,
  41. oldInfo,
  42. resolveContext,
  43. callback
  44. ) {
  45. (function findDescriptionFile() {
  46. if (oldInfo && oldInfo.directory === directory) {
  47. // We already have info for this directory and can reuse it
  48. return callback(null, oldInfo);
  49. }
  50. forEachBail(
  51. filenames,
  52. /**
  53. * @param {string} filename filename
  54. * @param {(err?: null|Error, result?: null|Result) => void} callback callback
  55. * @returns {void}
  56. */
  57. (filename, callback) => {
  58. const descriptionFilePath = resolver.join(directory, filename);
  59. if (resolver.fileSystem.readJson) {
  60. resolver.fileSystem.readJson(descriptionFilePath, (err, content) => {
  61. if (err) {
  62. if (typeof err.code !== "undefined") {
  63. if (resolveContext.missingDependencies) {
  64. resolveContext.missingDependencies.add(descriptionFilePath);
  65. }
  66. return callback();
  67. }
  68. if (resolveContext.fileDependencies) {
  69. resolveContext.fileDependencies.add(descriptionFilePath);
  70. }
  71. return onJson(err);
  72. }
  73. if (resolveContext.fileDependencies) {
  74. resolveContext.fileDependencies.add(descriptionFilePath);
  75. }
  76. onJson(null, /** @type {JsonObject} */ (content));
  77. });
  78. } else {
  79. resolver.fileSystem.readFile(descriptionFilePath, (err, content) => {
  80. if (err) {
  81. if (resolveContext.missingDependencies) {
  82. resolveContext.missingDependencies.add(descriptionFilePath);
  83. }
  84. return callback();
  85. }
  86. if (resolveContext.fileDependencies) {
  87. resolveContext.fileDependencies.add(descriptionFilePath);
  88. }
  89. /** @type {JsonObject | undefined} */
  90. let json;
  91. if (content) {
  92. try {
  93. json = JSON.parse(content.toString());
  94. } catch (/** @type {unknown} */ e) {
  95. return onJson(/** @type {Error} */ (e));
  96. }
  97. } else {
  98. return onJson(new Error("No content in file"));
  99. }
  100. onJson(null, json);
  101. });
  102. }
  103. /**
  104. * @param {null|Error} [err] error
  105. * @param {JsonObject} [content] content
  106. * @returns {void}
  107. */
  108. function onJson(err, content) {
  109. if (err) {
  110. if (resolveContext.log)
  111. resolveContext.log(
  112. descriptionFilePath + " (directory description file): " + err
  113. );
  114. else
  115. err.message =
  116. descriptionFilePath + " (directory description file): " + err;
  117. return callback(err);
  118. }
  119. callback(null, {
  120. content: /** @type {JsonObject} */ (content),
  121. directory,
  122. path: descriptionFilePath
  123. });
  124. }
  125. },
  126. /**
  127. * @param {null|Error} [err] error
  128. * @param {null|Result} [result] result
  129. * @returns {void}
  130. */
  131. (err, result) => {
  132. if (err) return callback(err);
  133. if (result) {
  134. return callback(null, result);
  135. } else {
  136. const dir = cdUp(directory);
  137. if (!dir) {
  138. return callback();
  139. } else {
  140. directory = dir;
  141. return findDescriptionFile();
  142. }
  143. }
  144. }
  145. );
  146. })();
  147. }
  148. /**
  149. * @param {JsonObject} content content
  150. * @param {string|string[]} field field
  151. * @returns {JsonValue | undefined} field data
  152. */
  153. function getField(content, field) {
  154. if (!content) return undefined;
  155. if (Array.isArray(field)) {
  156. /** @type {JsonValue} */
  157. let current = content;
  158. for (let j = 0; j < field.length; j++) {
  159. if (current === null || typeof current !== "object") {
  160. current = null;
  161. break;
  162. }
  163. current = /** @type {JsonObject} */ (current)[field[j]];
  164. }
  165. return current;
  166. } else {
  167. return content[field];
  168. }
  169. }
  170. /**
  171. * @param {string} directory directory
  172. * @returns {string|null} parent directory or null
  173. */
  174. function cdUp(directory) {
  175. if (directory === "/") return null;
  176. const i = directory.lastIndexOf("/"),
  177. j = directory.lastIndexOf("\\");
  178. const p = i < 0 ? j : j < 0 ? i : i < j ? j : i;
  179. if (p < 0) return null;
  180. return directory.slice(0, p || 1);
  181. }
  182. exports.loadDescriptionFile = loadDescriptionFile;
  183. exports.getField = getField;
  184. exports.cdUp = cdUp;