index.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. 'use strict';
  2. var fs = require('fs');
  3. var path = require('path');
  4. var isPlainObject = require('is-plain-object');
  5. var pick = require('object.pick');
  6. var defaults = require('object.defaults/immutable');
  7. var expandTilde = require('expand-tilde');
  8. var parsePath = require('parse-filepath');
  9. function fined(pathObj, defaultObj) {
  10. var expandedPath = expandPath(pathObj, defaultObj);
  11. return expandedPath ? findWithExpandedPath(expandedPath) : null;
  12. }
  13. function expandPath(pathObj, defaultObj) {
  14. if (!isPlainObject(defaultObj)) {
  15. defaultObj = {};
  16. }
  17. if (isString(pathObj)) {
  18. pathObj = { path: pathObj };
  19. }
  20. if (!isPlainObject(pathObj)) {
  21. pathObj = {};
  22. }
  23. pathObj = defaults(pathObj, defaultObj);
  24. var filePath;
  25. if (!isString(pathObj.path)) {
  26. return null;
  27. }
  28. // Execution of toString is for a String object.
  29. if (isString(pathObj.name) && pathObj.name) {
  30. if (pathObj.path) {
  31. filePath = expandTilde(pathObj.path.toString());
  32. filePath = path.join(filePath, pathObj.name.toString());
  33. } else {
  34. filePath = pathObj.name.toString();
  35. }
  36. } else {
  37. filePath = expandTilde(pathObj.path.toString());
  38. }
  39. var extArr = createExtensionArray(pathObj.extensions);
  40. var extMap = createExtensionMap(pathObj.extensions);
  41. var basedir = isString(pathObj.cwd) ? pathObj.cwd.toString() : '.';
  42. basedir = path.resolve(expandTilde(basedir));
  43. var findUp = !!pathObj.findUp;
  44. var parsed = parsePath(filePath);
  45. if (parsed.isAbsolute) {
  46. filePath = filePath.slice(parsed.root.length);
  47. findUp = false;
  48. basedir = parsed.root;
  49. /* istanbul ignore if */
  50. } else if (parsed.root) { // Expanded path has a drive letter on Windows.
  51. filePath = filePath.slice(parsed.root.length);
  52. basedir = path.resolve(parsed.root);
  53. }
  54. return {
  55. path: filePath,
  56. basedir: basedir,
  57. findUp: findUp,
  58. extArr: extArr,
  59. extMap: extMap,
  60. };
  61. }
  62. function findWithExpandedPath(expanded) {
  63. var found = expanded.findUp ?
  64. findUpFile(expanded.basedir, expanded.path, expanded.extArr) :
  65. findFile(expanded.basedir, expanded.path, expanded.extArr);
  66. if (!found) {
  67. return null;
  68. }
  69. if (expanded.extMap) {
  70. found.extension = pick(expanded.extMap, found.extension);
  71. }
  72. return found;
  73. }
  74. function findFile(basedir, relpath, extArr) {
  75. var noExtPath = path.resolve(basedir, relpath);
  76. for (var i = 0, n = extArr.length; i < n; i++) {
  77. var filepath = noExtPath + extArr[i];
  78. try {
  79. fs.statSync(filepath);
  80. return { path: filepath, extension: extArr[i] };
  81. } catch (e) {
  82. // Ignore error
  83. }
  84. }
  85. return null;
  86. }
  87. function findUpFile(basedir, filepath, extArr) {
  88. var lastdir;
  89. do {
  90. var found = findFile(basedir, filepath, extArr);
  91. if (found) {
  92. return found;
  93. }
  94. lastdir = basedir;
  95. basedir = path.dirname(basedir);
  96. } while (lastdir !== basedir);
  97. return null;
  98. }
  99. function createExtensionArray(exts) {
  100. if (isString(exts)) {
  101. return [exts];
  102. }
  103. if (Array.isArray(exts)) {
  104. exts = exts.filter(isString);
  105. return (exts.length > 0) ? exts : [''];
  106. }
  107. if (isPlainObject(exts)) {
  108. exts = Object.keys(exts);
  109. return (exts.length > 0) ? exts : [''];
  110. }
  111. return [''];
  112. }
  113. function createExtensionMap(exts) {
  114. if (!isPlainObject(exts)) {
  115. return null;
  116. }
  117. if (isEmpty(exts)) {
  118. return { '': null };
  119. }
  120. return exts;
  121. }
  122. function isEmpty(object) {
  123. return !Object.keys(object).length;
  124. }
  125. function isString(value) {
  126. if (typeof value === 'string') {
  127. return true;
  128. }
  129. if (Object.prototype.toString.call(value) === '[object String]') {
  130. return true;
  131. }
  132. return false;
  133. }
  134. module.exports = fined;