DescriptionFileUtils.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  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 (
  63. typeof (/** @type {NodeJS.ErrnoException} */ (err).code) !==
  64. "undefined"
  65. ) {
  66. if (resolveContext.missingDependencies) {
  67. resolveContext.missingDependencies.add(descriptionFilePath);
  68. }
  69. return callback();
  70. }
  71. if (resolveContext.fileDependencies) {
  72. resolveContext.fileDependencies.add(descriptionFilePath);
  73. }
  74. return onJson(err);
  75. }
  76. if (resolveContext.fileDependencies) {
  77. resolveContext.fileDependencies.add(descriptionFilePath);
  78. }
  79. onJson(null, content);
  80. });
  81. } else {
  82. resolver.fileSystem.readFile(descriptionFilePath, (err, content) => {
  83. if (err) {
  84. if (resolveContext.missingDependencies) {
  85. resolveContext.missingDependencies.add(descriptionFilePath);
  86. }
  87. return callback();
  88. }
  89. if (resolveContext.fileDependencies) {
  90. resolveContext.fileDependencies.add(descriptionFilePath);
  91. }
  92. /** @type {JsonObject | undefined} */
  93. let json;
  94. if (content) {
  95. try {
  96. json = JSON.parse(content.toString());
  97. } catch (/** @type {unknown} */ e) {
  98. return onJson(/** @type {Error} */ (e));
  99. }
  100. } else {
  101. return onJson(new Error("No content in file"));
  102. }
  103. onJson(null, json);
  104. });
  105. }
  106. /**
  107. * @param {null|Error} [err] error
  108. * @param {JsonObject} [content] content
  109. * @returns {void}
  110. */
  111. function onJson(err, content) {
  112. if (err) {
  113. if (resolveContext.log)
  114. resolveContext.log(
  115. descriptionFilePath + " (directory description file): " + err
  116. );
  117. else
  118. err.message =
  119. descriptionFilePath + " (directory description file): " + err;
  120. return callback(err);
  121. }
  122. callback(null, {
  123. content: /** @type {JsonObject} */ (content),
  124. directory,
  125. path: descriptionFilePath
  126. });
  127. }
  128. },
  129. /**
  130. * @param {null|Error} [err] error
  131. * @param {null|Result} [result] result
  132. * @returns {void}
  133. */
  134. (err, result) => {
  135. if (err) return callback(err);
  136. if (result) {
  137. return callback(null, result);
  138. } else {
  139. const dir = cdUp(directory);
  140. if (!dir) {
  141. return callback();
  142. } else {
  143. directory = dir;
  144. return findDescriptionFile();
  145. }
  146. }
  147. }
  148. );
  149. })();
  150. }
  151. /**
  152. * @param {JsonObject} content content
  153. * @param {string|string[]} field field
  154. * @returns {JsonValue | undefined} field data
  155. */
  156. function getField(content, field) {
  157. if (!content) return undefined;
  158. if (Array.isArray(field)) {
  159. /** @type {JsonValue} */
  160. let current = content;
  161. for (let j = 0; j < field.length; j++) {
  162. if (current === null || typeof current !== "object") {
  163. current = null;
  164. break;
  165. }
  166. current = /** @type {JsonObject} */ (current)[field[j]];
  167. }
  168. return current;
  169. } else {
  170. return content[field];
  171. }
  172. }
  173. /**
  174. * @param {string} directory directory
  175. * @returns {string|null} parent directory or null
  176. */
  177. function cdUp(directory) {
  178. if (directory === "/") return null;
  179. const i = directory.lastIndexOf("/"),
  180. j = directory.lastIndexOf("\\");
  181. const p = i < 0 ? j : j < 0 ? i : i < j ? j : i;
  182. if (p < 0) return null;
  183. return directory.slice(0, p || 1);
  184. }
  185. exports.loadDescriptionFile = loadDescriptionFile;
  186. exports.getField = getField;
  187. exports.cdUp = cdUp;