You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Walker.js 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.Walker = void 0;
  4. const debug = require("debug");
  5. const fs = require("fs-extra");
  6. const path = require("path");
  7. const depTypes_1 = require("./depTypes");
  8. const nativeModuleTypes_1 = require("./nativeModuleTypes");
  9. const d = debug('flora-colossus');
  10. class Walker {
  11. constructor(modulePath) {
  12. this.modules = [];
  13. this.walkHistory = new Set();
  14. this.cache = null;
  15. if (!modulePath || typeof modulePath !== 'string') {
  16. throw new Error('modulePath must be provided as a string');
  17. }
  18. d(`creating walker with rootModule=${modulePath}`);
  19. this.rootModule = modulePath;
  20. }
  21. relativeModule(rootPath, moduleName) {
  22. return path.resolve(rootPath, 'node_modules', moduleName);
  23. }
  24. async loadPackageJSON(modulePath) {
  25. const pJPath = path.resolve(modulePath, 'package.json');
  26. if (await fs.pathExists(pJPath)) {
  27. const pJ = await fs.readJson(pJPath);
  28. if (!pJ.dependencies)
  29. pJ.dependencies = {};
  30. if (!pJ.devDependencies)
  31. pJ.devDependencies = {};
  32. if (!pJ.optionalDependencies)
  33. pJ.optionalDependencies = {};
  34. return pJ;
  35. }
  36. return null;
  37. }
  38. async walkDependenciesForModuleInModule(moduleName, modulePath, depType) {
  39. let testPath = modulePath;
  40. let discoveredPath = null;
  41. let lastRelative = null;
  42. // Try find it while searching recursively up the tree
  43. while (!discoveredPath && this.relativeModule(testPath, moduleName) !== lastRelative) {
  44. lastRelative = this.relativeModule(testPath, moduleName);
  45. if (await fs.pathExists(lastRelative)) {
  46. discoveredPath = lastRelative;
  47. }
  48. else {
  49. if (path.basename(path.dirname(testPath)) !== 'node_modules') {
  50. testPath = path.dirname(testPath);
  51. }
  52. testPath = path.dirname(path.dirname(testPath));
  53. }
  54. }
  55. // If we can't find it the install is probably buggered
  56. if (!discoveredPath && depType !== depTypes_1.DepType.OPTIONAL && depType !== depTypes_1.DepType.DEV_OPTIONAL) {
  57. throw new Error(`Failed to locate module "${moduleName}" from "${modulePath}"
  58. This normally means that either you have deleted this package already somehow (check your ignore settings if using electron-packager). Or your module installation failed.`);
  59. }
  60. // If we can find it let's do the same thing for that module
  61. if (discoveredPath) {
  62. await this.walkDependenciesForModule(discoveredPath, depType);
  63. }
  64. }
  65. async detectNativeModuleType(modulePath, pJ) {
  66. if (pJ.dependencies['prebuild-install']) {
  67. return nativeModuleTypes_1.NativeModuleType.PREBUILD;
  68. }
  69. else if (await fs.pathExists(path.join(modulePath, 'binding.gyp'))) {
  70. return nativeModuleTypes_1.NativeModuleType.NODE_GYP;
  71. }
  72. return nativeModuleTypes_1.NativeModuleType.NONE;
  73. }
  74. async walkDependenciesForModule(modulePath, depType) {
  75. d('walk reached:', modulePath, ' Type is:', depTypes_1.DepType[depType]);
  76. // We have already traversed this module
  77. if (this.walkHistory.has(modulePath)) {
  78. d('already walked this route');
  79. // Find the existing module reference
  80. const existingModule = this.modules.find(module => module.path === modulePath);
  81. // If the depType we are traversing with now is higher than the
  82. // last traversal then update it (prod superseeds dev for instance)
  83. if ((0, depTypes_1.depTypeGreater)(depType, existingModule.depType)) {
  84. d(`existing module has a type of "${existingModule.depType}", new module type would be "${depType}" therefore updating`);
  85. existingModule.depType = depType;
  86. }
  87. return;
  88. }
  89. const pJ = await this.loadPackageJSON(modulePath);
  90. // If the module doesn't have a package.json file it is probably a
  91. // dead install from yarn (they dont clean up for some reason)
  92. if (!pJ) {
  93. d('walk hit a dead end, this module is incomplete');
  94. return;
  95. }
  96. // Record this module as being traversed
  97. this.walkHistory.add(modulePath);
  98. this.modules.push({
  99. depType,
  100. nativeModuleType: await this.detectNativeModuleType(modulePath, pJ),
  101. path: modulePath,
  102. name: pJ.name,
  103. });
  104. // For every prod dep
  105. for (const moduleName in pJ.dependencies) {
  106. // npm decides it's a funny thing to put optional dependencies in the "dependencies" section
  107. // after install, because that makes perfect sense
  108. if (moduleName in pJ.optionalDependencies) {
  109. d(`found ${moduleName} in prod deps of ${modulePath} but it is also marked optional`);
  110. continue;
  111. }
  112. await this.walkDependenciesForModuleInModule(moduleName, modulePath, (0, depTypes_1.childDepType)(depType, depTypes_1.DepType.PROD));
  113. }
  114. // For every optional dep
  115. for (const moduleName in pJ.optionalDependencies) {
  116. await this.walkDependenciesForModuleInModule(moduleName, modulePath, (0, depTypes_1.childDepType)(depType, depTypes_1.DepType.OPTIONAL));
  117. }
  118. // For every dev dep, but only if we are in the root module
  119. if (depType === depTypes_1.DepType.ROOT) {
  120. d('we\'re still at the beginning, walking down the dev route');
  121. for (const moduleName in pJ.devDependencies) {
  122. await this.walkDependenciesForModuleInModule(moduleName, modulePath, (0, depTypes_1.childDepType)(depType, depTypes_1.DepType.DEV));
  123. }
  124. }
  125. }
  126. async walkTree() {
  127. d('starting tree walk');
  128. if (!this.cache) {
  129. this.cache = new Promise(async (resolve, reject) => {
  130. this.modules = [];
  131. try {
  132. await this.walkDependenciesForModule(this.rootModule, depTypes_1.DepType.ROOT);
  133. }
  134. catch (err) {
  135. reject(err);
  136. return;
  137. }
  138. resolve(this.modules);
  139. });
  140. }
  141. else {
  142. d('tree walk in progress / completed already, waiting for existing walk to complete');
  143. }
  144. return await this.cache;
  145. }
  146. getRootModule() {
  147. return this.rootModule;
  148. }
  149. }
  150. exports.Walker = Walker;
  151. //# sourceMappingURL=Walker.js.map