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