DescriptionFileUtils.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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").ResolveContext} ResolveContext */
  9. /**
  10. * @typedef {Object} DescriptionFileInfo
  11. * @property {any=} content
  12. * @property {string} path
  13. * @property {string} directory
  14. */
  15. /**
  16. * @callback ErrorFirstCallback
  17. * @param {Error|null=} error
  18. * @param {DescriptionFileInfo=} result
  19. */
  20. /**
  21. * @param {Resolver} resolver resolver
  22. * @param {string} directory directory
  23. * @param {string[]} filenames filenames
  24. * @param {DescriptionFileInfo|undefined} oldInfo oldInfo
  25. * @param {ResolveContext} resolveContext resolveContext
  26. * @param {ErrorFirstCallback} callback callback
  27. */
  28. function loadDescriptionFile(
  29. resolver,
  30. directory,
  31. filenames,
  32. oldInfo,
  33. resolveContext,
  34. callback
  35. ) {
  36. (function findDescriptionFile() {
  37. if (oldInfo && oldInfo.directory === directory) {
  38. // We already have info for this directory and can reuse it
  39. return callback(null, oldInfo);
  40. }
  41. forEachBail(
  42. filenames,
  43. (filename, callback) => {
  44. const descriptionFilePath = resolver.join(directory, filename);
  45. if (resolver.fileSystem.readJson) {
  46. resolver.fileSystem.readJson(descriptionFilePath, (err, content) => {
  47. if (err) {
  48. if (typeof err.code !== "undefined") {
  49. if (resolveContext.missingDependencies) {
  50. resolveContext.missingDependencies.add(descriptionFilePath);
  51. }
  52. return callback();
  53. }
  54. if (resolveContext.fileDependencies) {
  55. resolveContext.fileDependencies.add(descriptionFilePath);
  56. }
  57. return onJson(err);
  58. }
  59. if (resolveContext.fileDependencies) {
  60. resolveContext.fileDependencies.add(descriptionFilePath);
  61. }
  62. onJson(null, content);
  63. });
  64. } else {
  65. resolver.fileSystem.readFile(descriptionFilePath, (err, content) => {
  66. if (err) {
  67. if (resolveContext.missingDependencies) {
  68. resolveContext.missingDependencies.add(descriptionFilePath);
  69. }
  70. return callback();
  71. }
  72. if (resolveContext.fileDependencies) {
  73. resolveContext.fileDependencies.add(descriptionFilePath);
  74. }
  75. let json;
  76. if (content) {
  77. try {
  78. json = JSON.parse(content.toString());
  79. } catch (e) {
  80. return onJson(e);
  81. }
  82. } else {
  83. return onJson(new Error("No content in file"));
  84. }
  85. onJson(null, json);
  86. });
  87. }
  88. function onJson(err, content) {
  89. if (err) {
  90. if (resolveContext.log)
  91. resolveContext.log(
  92. descriptionFilePath + " (directory description file): " + err
  93. );
  94. else
  95. err.message =
  96. descriptionFilePath + " (directory description file): " + err;
  97. return callback(err);
  98. }
  99. callback(null, {
  100. content,
  101. directory,
  102. path: descriptionFilePath
  103. });
  104. }
  105. },
  106. (err, result) => {
  107. if (err) return callback(err);
  108. if (result) {
  109. return callback(null, result);
  110. } else {
  111. const dir = cdUp(directory);
  112. if (!dir) {
  113. return callback();
  114. } else {
  115. directory = dir;
  116. return findDescriptionFile();
  117. }
  118. }
  119. }
  120. );
  121. })();
  122. }
  123. /**
  124. * @param {any} content content
  125. * @param {string|string[]} field field
  126. * @returns {object|string|number|boolean|undefined} field data
  127. */
  128. function getField(content, field) {
  129. if (!content) return undefined;
  130. if (Array.isArray(field)) {
  131. let current = content;
  132. for (let j = 0; j < field.length; j++) {
  133. if (current === null || typeof current !== "object") {
  134. current = null;
  135. break;
  136. }
  137. current = current[field[j]];
  138. }
  139. return current;
  140. } else {
  141. return content[field];
  142. }
  143. }
  144. /**
  145. * @param {string} directory directory
  146. * @returns {string|null} parent directory or null
  147. */
  148. function cdUp(directory) {
  149. if (directory === "/") return null;
  150. const i = directory.lastIndexOf("/"),
  151. j = directory.lastIndexOf("\\");
  152. const p = i < 0 ? j : j < 0 ? i : i < j ? j : i;
  153. if (p < 0) return null;
  154. return directory.substr(0, p || 1);
  155. }
  156. exports.loadDescriptionFile = loadDescriptionFile;
  157. exports.getField = getField;
  158. exports.cdUp = cdUp;