createExplorer.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. //
  2. 'use strict';
  3. const path = require('path');
  4. const loadPackageProp = require('./loadPackageProp');
  5. const loadRc = require('./loadRc');
  6. const loadJs = require('./loadJs');
  7. const loadDefinedFile = require('./loadDefinedFile');
  8. const funcRunner = require('./funcRunner');
  9. const getDirectory = require('./getDirectory');
  10. module.exports = function createExplorer(options
  11. ) {
  12. // When `options.sync` is `false` (default),
  13. // these cache Promises that resolve with results, not the results themselves.
  14. const fileCache = options.cache ? new Map() : null;
  15. const directoryCache = options.cache ? new Map() : null;
  16. const transform = options.transform || identity;
  17. const packageProp = options.packageProp;
  18. function clearFileCache() {
  19. if (fileCache) fileCache.clear();
  20. }
  21. function clearDirectoryCache() {
  22. if (directoryCache) directoryCache.clear();
  23. }
  24. function clearCaches() {
  25. clearFileCache();
  26. clearDirectoryCache();
  27. }
  28. function throwError(error) {
  29. if (options.sync) {
  30. throw error;
  31. } else {
  32. return Promise.reject(error);
  33. }
  34. }
  35. function load(
  36. searchPath ,
  37. configPath
  38. ) {
  39. if (!searchPath) searchPath = process.cwd();
  40. if (!configPath && options.configPath) configPath = options.configPath;
  41. if (configPath) {
  42. const absoluteConfigPath = path.resolve(process.cwd(), configPath);
  43. if (fileCache && fileCache.has(absoluteConfigPath)) {
  44. return fileCache.get(absoluteConfigPath);
  45. }
  46. let load;
  47. if (path.basename(absoluteConfigPath) === 'package.json') {
  48. if (!packageProp) {
  49. return throwError(
  50. new Error(
  51. 'Please specify the packageProp option. The configPath argument cannot point to a package.json file if packageProp is false.'
  52. )
  53. );
  54. }
  55. load = () =>
  56. loadPackageProp(path.dirname(absoluteConfigPath), {
  57. packageProp,
  58. sync: options.sync,
  59. });
  60. } else {
  61. load = () =>
  62. loadDefinedFile(absoluteConfigPath, {
  63. sync: options.sync,
  64. format: options.format,
  65. });
  66. }
  67. const loadResult = load();
  68. const result =
  69. loadResult instanceof Promise
  70. ? loadResult.then(transform)
  71. : transform(loadResult);
  72. if (fileCache) fileCache.set(absoluteConfigPath, result);
  73. return result;
  74. }
  75. const absoluteSearchPath = path.resolve(process.cwd(), searchPath);
  76. const searchPathDir = getDirectory(absoluteSearchPath, options.sync);
  77. return searchPathDir instanceof Promise
  78. ? searchPathDir.then(searchDirectory)
  79. : searchDirectory(searchPathDir);
  80. }
  81. function searchDirectory(
  82. directory
  83. ) {
  84. if (directoryCache && directoryCache.has(directory)) {
  85. return directoryCache.get(directory);
  86. }
  87. const result = funcRunner(!options.sync ? Promise.resolve() : undefined, [
  88. () => {
  89. if (!packageProp) return;
  90. return loadPackageProp(directory, {
  91. packageProp,
  92. sync: options.sync,
  93. });
  94. },
  95. result => {
  96. if (result || !options.rc) return result;
  97. return loadRc(path.join(directory, options.rc), {
  98. sync: options.sync,
  99. rcStrictJson: options.rcStrictJson,
  100. rcExtensions: options.rcExtensions,
  101. });
  102. },
  103. result => {
  104. if (result || !options.js) return result;
  105. return loadJs(path.join(directory, options.js), { sync: options.sync });
  106. },
  107. result => {
  108. if (result) return result;
  109. const nextDirectory = path.dirname(directory);
  110. if (nextDirectory === directory || directory === options.stopDir)
  111. return null;
  112. return searchDirectory(nextDirectory);
  113. },
  114. transform,
  115. ]);
  116. if (directoryCache) directoryCache.set(directory, result);
  117. return result;
  118. }
  119. return {
  120. load,
  121. clearFileCache,
  122. clearDirectoryCache,
  123. clearCaches,
  124. };
  125. };
  126. function identity(x) {
  127. return x;
  128. }