├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitattributes ├── .github └── workflows │ ├── dev.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── package.json └── test ├── fixtures └── fined │ ├── .js │ └── app.js │ ├── app.js │ ├── appfile.js │ ├── appfile.json │ ├── index.json │ └── package.json ├── index.js └── utils ├── create-symlinks.js └── get-userhome-file.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | end_of_line = lf 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "gulp", 3 | "rules": { 4 | "max-len": 0, 5 | "max-statements": 0 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/dev.yml: -------------------------------------------------------------------------------- 1 | name: dev 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - master 7 | - main 8 | env: 9 | CI: true 10 | 11 | jobs: 12 | prettier: 13 | name: Format code 14 | runs-on: ubuntu-latest 15 | if: ${{ github.event_name == 'push' }} 16 | 17 | steps: 18 | - name: Checkout 19 | uses: actions/checkout@v2 20 | 21 | - name: Prettier 22 | uses: gulpjs/prettier_action@v3.0 23 | with: 24 | commit_message: 'chore: Run prettier' 25 | prettier_options: '--write .' 26 | 27 | test: 28 | name: Tests for Node ${{ matrix.node }} on ${{ matrix.os }} 29 | runs-on: ${{ matrix.os }} 30 | 31 | strategy: 32 | fail-fast: false 33 | matrix: 34 | node: [10, 12, 14, 16] 35 | os: [ubuntu-latest, windows-latest, macos-latest] 36 | 37 | steps: 38 | - name: Clone repository 39 | uses: actions/checkout@v2 40 | 41 | - name: Set Node.js version 42 | uses: actions/setup-node@v2 43 | with: 44 | node-version: ${{ matrix.node }} 45 | 46 | - run: node --version 47 | - run: npm --version 48 | 49 | - name: Install npm dependencies 50 | run: npm install 51 | 52 | - name: Run lint 53 | run: npm run lint 54 | 55 | - name: Run tests 56 | run: npm test 57 | 58 | - name: Coveralls 59 | uses: coverallsapp/github-action@v1.1.2 60 | with: 61 | github-token: ${{ secrets.GITHUB_TOKEN }} 62 | flag-name: ${{ matrix.os }}-node-${{ matrix.node }} 63 | parallel: true 64 | 65 | coveralls: 66 | needs: test 67 | name: Finish up 68 | 69 | runs-on: ubuntu-latest 70 | steps: 71 | - name: Coveralls Finished 72 | uses: coverallsapp/github-action@v1.1.2 73 | with: 74 | github-token: ${{ secrets.GITHUB_TOKEN }} 75 | parallel-finished: true 76 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | push: 4 | branches: 5 | - master 6 | - main 7 | 8 | jobs: 9 | release-please: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: GoogleCloudPlatform/release-please-action@v2 13 | with: 14 | token: ${{ secrets.GITHUB_TOKEN }} 15 | release-type: node 16 | package-name: release-please-action 17 | bump-minor-pre-major: true 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # Garbage files 64 | .DS_Store 65 | test/fixtures/fined/symlink* 66 | 67 | # Test results 68 | test.xunit 69 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | .nyc_output/ 3 | CHANGELOG.md 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [2.0.0](https://www.github.com/gulpjs/fined/compare/v1.2.0...v2.0.0) (2021-10-31) 4 | 5 | 6 | ### ⚠ BREAKING CHANGES 7 | 8 | * Normalize repository, dropping node <10.13 support (#12) 9 | 10 | ### Bug Fixes 11 | 12 | * Ensure all tests run on Windows + Node 10 ([#14](https://www.github.com/gulpjs/fined/issues/14)) ([857a564](https://www.github.com/gulpjs/fined/commit/857a56482c39413bb7891355f039659aeb86c46b)) 13 | 14 | 15 | ### Miscellaneous Chores 16 | 17 | * Normalize repository, dropping node <10.13 support ([#12](https://www.github.com/gulpjs/fined/issues/12)) ([221118c](https://www.github.com/gulpjs/fined/commit/221118c0c0d0cde2c78682351545a0592be31938)) 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016, 2017, 2018, 2021 Blaine Bublitz and Eric Schoffstall 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 | # fined 8 | 9 | [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][ci-image]][ci-url] [![Coveralls Status][coveralls-image]][coveralls-url] 10 | 11 | Find a file given a declaration of locations. 12 | 13 | ## Usage 14 | 15 | ```js 16 | var fined = require('fined'); 17 | 18 | fined({ path: 'path/to/file', extensions: ['.js', '.json'] }); 19 | // => { path: '/absolute/path/to/file.js', extension: '.js' } (if file exists) 20 | // => null (if file does not exist) 21 | 22 | var opts = { 23 | name: '.app', 24 | cwd: '.', 25 | extensions: { 26 | rc: 'default-rc-loader', 27 | '.yml': 'default-yml-loader', 28 | }, 29 | }; 30 | 31 | fined({ path: '.' }, opts); 32 | // => { path: '/absolute/of/cwd/.app.yml', extension: { '.yml': 'default-yml-loader' } } 33 | 34 | fined({ path: '~', extensions: { rc: 'some-special-rc-loader' } }, opts); 35 | // => { path: '/User/home/.apprc', extension: { 'rc': 'some-special-rc-loader' } } 36 | ``` 37 | 38 | ## API 39 | 40 | ### fined(pathObj, opts) => object | null 41 | 42 | #### Arguments: 43 | 44 | - **pathObj** [string | object] : a path setting for finding a file. 45 | - **opts** [object] : a plain object supplements `pathObj`. 46 | 47 | `pathObj` and `opts` can have same properties: 48 | 49 | - **path** [string] : a path string. 50 | - **name** [string] : a basename. 51 | - **extensions**: [string | array | object] : extensions. 52 | - **cwd**: a base directory of `path` and for finding up. 53 | - **findUp**: [boolean] : a flag to find up. 54 | 55 | #### Return: 56 | 57 | This function returns a plain object which consists of following properties if a file exists otherwise null. 58 | 59 | - **path** : an absolute path 60 | - **extension** : a string or a plain object of extension. 61 | 62 | ## License 63 | 64 | MIT 65 | 66 | 67 | [downloads-image]: https://img.shields.io/npm/dm/fined.svg?style=flat-square 68 | [npm-url]: https://www.npmjs.com/package/fined 69 | [npm-image]: https://img.shields.io/npm/v/fined.svg?style=flat-square 70 | 71 | [ci-url]: https://github.com/gulpjs/fined/actions?query=workflow:dev 72 | [ci-image]: https://img.shields.io/github/workflow/status/gulpjs/fined/dev?style=flat-square 73 | 74 | [coveralls-url]: https://coveralls.io/r/gulpjs/fined 75 | [coveralls-image]: https://img.shields.io/coveralls/gulpjs/fined/master.svg 76 | 77 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | 6 | var isPlainObject = require('is-plain-object').isPlainObject; 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 | function fined(pathObj, defaultObj) { 13 | var expandedPath = expandPath(pathObj, defaultObj); 14 | return expandedPath ? findWithExpandedPath(expandedPath) : null; 15 | } 16 | 17 | function expandPath(pathObj, defaultObj) { 18 | if (!isPlainObject(defaultObj)) { 19 | defaultObj = {}; 20 | } 21 | 22 | if (isString(pathObj)) { 23 | pathObj = { path: pathObj }; 24 | } 25 | 26 | if (!isPlainObject(pathObj)) { 27 | pathObj = {}; 28 | } 29 | 30 | pathObj = defaults(pathObj, defaultObj); 31 | 32 | var filePath; 33 | if (!isString(pathObj.path)) { 34 | return null; 35 | } 36 | // Execution of toString is for a String object. 37 | if (isString(pathObj.name) && pathObj.name) { 38 | if (pathObj.path) { 39 | filePath = expandTilde(pathObj.path.toString()); 40 | filePath = path.join(filePath, pathObj.name.toString()); 41 | } else { 42 | filePath = pathObj.name.toString(); 43 | } 44 | } else { 45 | filePath = expandTilde(pathObj.path.toString()); 46 | } 47 | 48 | var extArr = createExtensionArray(pathObj.extensions); 49 | var extMap = createExtensionMap(pathObj.extensions); 50 | 51 | var basedir = isString(pathObj.cwd) ? pathObj.cwd.toString() : '.'; 52 | basedir = path.resolve(expandTilde(basedir)); 53 | 54 | var findUp = !!pathObj.findUp; 55 | 56 | var parsed = parsePath(filePath); 57 | if (parsed.isAbsolute) { 58 | filePath = filePath.slice(parsed.root.length); 59 | findUp = false; 60 | basedir = parsed.root; 61 | } /* istanbul ignore next */ else if (parsed.root) { 62 | // Expanded path has a drive letter on Windows. 63 | filePath = filePath.slice(parsed.root.length); 64 | basedir = path.resolve(parsed.root); 65 | } 66 | 67 | if (parsed.ext) { 68 | filePath = filePath.slice(0, -parsed.ext.length); 69 | // This ensures that only the original extension is matched. 70 | extArr = [parsed.ext]; 71 | } 72 | 73 | return { 74 | path: filePath, 75 | basedir: basedir, 76 | findUp: findUp, 77 | extArr: extArr, 78 | extMap: extMap, 79 | }; 80 | } 81 | 82 | function findWithExpandedPath(expanded) { 83 | var found = expanded.findUp 84 | ? findUpFile(expanded.basedir, expanded.path, expanded.extArr) 85 | : findFile(expanded.basedir, expanded.path, expanded.extArr); 86 | 87 | if (!found) { 88 | return null; 89 | } 90 | 91 | if (expanded.extMap) { 92 | found.extension = pick(expanded.extMap, found.extension); 93 | } 94 | return found; 95 | } 96 | 97 | function findFile(basedir, relpath, extArr) { 98 | var noExtPath = path.resolve(basedir, relpath); 99 | for (var i = 0, n = extArr.length; i < n; i++) { 100 | var filepath = noExtPath + extArr[i]; 101 | try { 102 | fs.statSync(filepath); 103 | return { path: filepath, extension: extArr[i] }; 104 | } catch (e) { 105 | // Ignore error 106 | } 107 | } 108 | 109 | return null; 110 | } 111 | 112 | function findUpFile(basedir, filepath, extArr) { 113 | var lastdir; 114 | do { 115 | var found = findFile(basedir, filepath, extArr); 116 | if (found) { 117 | return found; 118 | } 119 | 120 | lastdir = basedir; 121 | basedir = path.dirname(basedir); 122 | } while (lastdir !== basedir); 123 | 124 | return null; 125 | } 126 | 127 | function createExtensionArray(exts) { 128 | if (isString(exts)) { 129 | return [exts]; 130 | } 131 | 132 | if (Array.isArray(exts)) { 133 | exts = exts.filter(isString); 134 | return exts.length > 0 ? exts : ['']; 135 | } 136 | 137 | if (isPlainObject(exts)) { 138 | exts = Object.keys(exts); 139 | return exts.length > 0 ? exts : ['']; 140 | } 141 | 142 | return ['']; 143 | } 144 | 145 | function createExtensionMap(exts) { 146 | if (!isPlainObject(exts)) { 147 | return null; 148 | } 149 | 150 | if (isEmpty(exts)) { 151 | return { '': null }; 152 | } 153 | 154 | return exts; 155 | } 156 | 157 | function isEmpty(object) { 158 | return !Object.keys(object).length; 159 | } 160 | 161 | function isString(value) { 162 | if (typeof value === 'string') { 163 | return true; 164 | } 165 | 166 | if (Object.prototype.toString.call(value) === '[object String]') { 167 | return true; 168 | } 169 | 170 | return false; 171 | } 172 | 173 | module.exports = fined; 174 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fined", 3 | "version": "2.0.0", 4 | "description": "Find a file given a declaration of locations.", 5 | "author": "Gulp Team (https://gulpjs.com/)", 6 | "contributors": [ 7 | "Takayuki Sato ", 8 | "Blaine Bublitz " 9 | ], 10 | "repository": "gulpjs/fined", 11 | "license": "MIT", 12 | "engines": { 13 | "node": ">= 10.13.0" 14 | }, 15 | "main": "index.js", 16 | "files": [ 17 | "index.js", 18 | "LICENSE" 19 | ], 20 | "scripts": { 21 | "lint": "eslint .", 22 | "pretest": "npm run lint", 23 | "test": "nyc mocha --async-only" 24 | }, 25 | "dependencies": { 26 | "expand-tilde": "^2.0.2", 27 | "is-plain-object": "^5.0.0", 28 | "object.defaults": "^1.1.0", 29 | "object.pick": "^1.3.0", 30 | "parse-filepath": "^1.0.2" 31 | }, 32 | "devDependencies": { 33 | "eslint": "^7.32.0", 34 | "eslint-config-gulp": "^5.0.1", 35 | "eslint-plugin-node": "^11.1.0", 36 | "expect": "^27.3.1", 37 | "mocha": "^8.4.0", 38 | "nyc": "^15.1.0" 39 | }, 40 | "nyc": { 41 | "reporter": [ 42 | "lcov", 43 | "text-summary" 44 | ] 45 | }, 46 | "prettier": { 47 | "singleQuote": true 48 | }, 49 | "keywords": [ 50 | "find", 51 | "lookup", 52 | "config" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /test/fixtures/fined/.js/app.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gulpjs/fined/5e810a6ad3320b3bfac72e6284aafc2ac01cf213/test/fixtures/fined/.js/app.js -------------------------------------------------------------------------------- /test/fixtures/fined/app.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gulpjs/fined/5e810a6ad3320b3bfac72e6284aafc2ac01cf213/test/fixtures/fined/app.js -------------------------------------------------------------------------------- /test/fixtures/fined/appfile.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gulpjs/fined/5e810a6ad3320b3bfac72e6284aafc2ac01cf213/test/fixtures/fined/appfile.js -------------------------------------------------------------------------------- /test/fixtures/fined/appfile.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gulpjs/fined/5e810a6ad3320b3bfac72e6284aafc2ac01cf213/test/fixtures/fined/appfile.json -------------------------------------------------------------------------------- /test/fixtures/fined/index.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gulpjs/fined/5e810a6ad3320b3bfac72e6284aafc2ac01cf213/test/fixtures/fined/index.json -------------------------------------------------------------------------------- /test/fixtures/fined/package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var os = require('os'); 4 | var path = require('path'); 5 | var expect = require('expect'); 6 | 7 | var cwd = process.cwd(); 8 | var isWindows = os.platform() === 'win32'; 9 | 10 | var userHomeFile = require('./utils/get-userhome-file'); 11 | var symlinkedFiles = require('./utils/create-symlinks'); 12 | 13 | var fined = require('../'); 14 | 15 | if (isWindows) { 16 | // Workaround for differnce between path.resolve(winDrive) and process.cwd() 17 | process.chdir(process.cwd()); 18 | } 19 | 20 | describe('Basic behaviors', function () { 21 | it('returns object when target file exists', function (done) { 22 | var pathObj = { 23 | path: 'test/fixtures/fined', 24 | extensions: ['.json', '.js'], 25 | }; 26 | 27 | var defaultObj = { 28 | name: 'app', 29 | cwd: cwd, 30 | }; 31 | 32 | var expected = { 33 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 34 | extension: '.js', 35 | }; 36 | 37 | var result = fined(pathObj, defaultObj); 38 | 39 | expect(result).toEqual(expected); 40 | done(); 41 | }); 42 | 43 | it('returns null when target file does not exist', function (done) { 44 | var pathObj = { 45 | path: 'test/fixtures/fined', 46 | extensions: ['.json', '.js'], 47 | }; 48 | 49 | var defaultObj = { 50 | name: 'aaa', 51 | cwd: cwd, 52 | }; 53 | 54 | var expected = null; 55 | 56 | var result = fined(pathObj, defaultObj); 57 | 58 | expect(result).toEqual(expected); 59 | done(); 60 | }); 61 | 62 | it('normalizes a string as 1st argument to an object', function (done) { 63 | var pathObj = 'test/fixtures/fined'; 64 | 65 | var defaultObj = { 66 | name: 'app', 67 | extensions: ['.json', '.js'], 68 | cwd: cwd, 69 | findUp: false, 70 | }; 71 | 72 | var expected = { 73 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 74 | extension: '.js', 75 | }; 76 | 77 | var result = fined(pathObj, defaultObj); 78 | 79 | expect(result).toEqual(expected); 80 | done(); 81 | }); 82 | 83 | it('returns null when both arguments are empty', function (done) { 84 | var pathObj = {}; 85 | 86 | var defaultObj = {}; 87 | 88 | var expected = null; 89 | 90 | var result = fined(pathObj, defaultObj); 91 | 92 | expect(result).toEqual(expected); 93 | done(); 94 | }); 95 | 96 | it('returns null when both arguments are null', function (done) { 97 | var pathObj = null; 98 | 99 | var defaultObj = null; 100 | 101 | var expected = null; 102 | 103 | var result = fined(pathObj, defaultObj); 104 | 105 | expect(result).toEqual(expected); 106 | done(); 107 | }); 108 | 109 | it('treats 1st argument as an empty object when it is invalid', function (done) { 110 | var pathObj = 123; 111 | 112 | var defaultObj = { 113 | path: 'test/fixtures/fined', 114 | name: 'app', 115 | extensions: ['.json', '.js'], 116 | cwd: cwd, 117 | findUp: false, 118 | }; 119 | 120 | var expected = { 121 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 122 | extension: '.js', 123 | }; 124 | 125 | var result = fined(pathObj, defaultObj); 126 | 127 | expect(result).toEqual(expected); 128 | done(); 129 | }); 130 | 131 | it('returns null when both arguments are invalid', function (done) { 132 | var pathObj = function () { 133 | return { 134 | path: 'test/fixtures/fined', 135 | name: 'app', 136 | extensions: ['.json', '.js'], 137 | cwd: cwd, 138 | findUp: false, 139 | }; 140 | }; 141 | 142 | var defaultObj = true; 143 | 144 | var expected = null; 145 | 146 | var result = fined(pathObj, defaultObj); 147 | 148 | expect(result).toEqual(expected); 149 | done(); 150 | }); 151 | 152 | it('accepts paths with extensions already', function (done) { 153 | var pathObj = { 154 | path: 'test/fixtures/fined/app.js', 155 | cwd: cwd, 156 | extensions: ['.json', '.js'], 157 | }; 158 | 159 | var expected = { 160 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 161 | extension: '.js', 162 | }; 163 | 164 | var result = fined(pathObj); 165 | 166 | expect(result).toEqual(expected); 167 | done(); 168 | }); 169 | 170 | it('only matches the extension specified in path', function (done) { 171 | var pathObj = { 172 | path: 'test/fixtures/fined/appfile.js', 173 | cwd: cwd, 174 | extensions: ['.json', '.js'], 175 | }; 176 | 177 | var expected = { 178 | path: path.resolve(cwd, 'test/fixtures/fined', 'appfile.js'), 179 | extension: '.js', 180 | }; 181 | 182 | var result = fined(pathObj); 183 | 184 | expect(result).toEqual(expected); 185 | done(); 186 | }); 187 | 188 | it('accepts name with extensions already', function (done) { 189 | var pathObj = { 190 | path: 'test/fixtures/fined', 191 | extensions: ['.json', '.js'], 192 | }; 193 | 194 | var defaultObj = { 195 | name: 'app.js', 196 | cwd: cwd, 197 | }; 198 | 199 | var expected = { 200 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 201 | extension: '.js', 202 | }; 203 | 204 | var result = fined(pathObj, defaultObj); 205 | 206 | expect(result).toEqual(expected); 207 | done(); 208 | }); 209 | 210 | it('only matches the extension specified in name', function (done) { 211 | var pathObj = { 212 | path: 'test/fixtures/fined', 213 | extensions: ['.json', '.js'], 214 | }; 215 | 216 | var defaultObj = { 217 | name: 'appfile.js', 218 | cwd: cwd, 219 | }; 220 | 221 | var expected = { 222 | path: path.resolve(cwd, 'test/fixtures/fined', 'appfile.js'), 223 | extension: '.js', 224 | }; 225 | 226 | var result = fined(pathObj, defaultObj); 227 | 228 | expect(result).toEqual(expected); 229 | done(); 230 | }); 231 | 232 | it('only ignores the extension at the end of the path', function (done) { 233 | var pathObj = { 234 | path: 'test/fixtures/fined/.js/app.js', 235 | cwd: cwd, 236 | extensions: ['.json', '.js'], 237 | }; 238 | 239 | var expected = { 240 | path: path.resolve(cwd, 'test/fixtures/fined/.js', 'app.js'), 241 | extension: '.js', 242 | }; 243 | 244 | var result = fined(pathObj); 245 | 246 | expect(result).toEqual(expected); 247 | done(); 248 | }); 249 | }); 250 | 251 | describe('Argument defaulting', function () { 252 | it('does not default when 2nd argument is empty', function (done) { 253 | var pathObj = { 254 | name: 'package', 255 | path: 'test/fixtures/fined', 256 | extensions: ['.json', '.js'], 257 | cwd: cwd, 258 | findUp: false, 259 | }; 260 | 261 | var defaultObj = {}; 262 | 263 | var expected = { 264 | path: path.resolve(cwd, 'test/fixtures/fined', 'package.json'), 265 | extension: '.json', 266 | }; 267 | 268 | var result = fined(pathObj, defaultObj); 269 | 270 | expect(result).toEqual(expected); 271 | done(); 272 | }); 273 | 274 | it('defaults all properties when 1st argument is empty', function (done) { 275 | var pathObj = {}; 276 | 277 | var defaultObj = { 278 | name: 'package', 279 | path: 'test/fixtures/fined', 280 | extensions: ['.json', '.js'], 281 | cwd: cwd, 282 | findUp: false, 283 | }; 284 | 285 | var expected = { 286 | path: path.resolve(cwd, 'test/fixtures/fined', 'package.json'), 287 | extension: '.json', 288 | }; 289 | 290 | var result = fined(pathObj, defaultObj); 291 | 292 | expect(result).toEqual(expected); 293 | done(); 294 | }); 295 | 296 | it('defaults missing properties in 1st argument', function (done) { 297 | var pathObj = { 298 | name: 'app', 299 | cwd: path.resolve(cwd, 'test'), 300 | }; 301 | 302 | var defaultObj = { 303 | path: 'fixtures/fined', 304 | extensions: ['.json', '.js'], 305 | findUp: false, 306 | }; 307 | 308 | var expected = { 309 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 310 | extension: '.js', 311 | }; 312 | 313 | var result = fined(pathObj, defaultObj); 314 | 315 | expect(result).toEqual(expected); 316 | done(); 317 | }); 318 | 319 | it('defaults null properties in the 1st argument', function (done) { 320 | var pathObj = { 321 | name: null, 322 | path: null, 323 | extensions: null, 324 | cwd: null, 325 | findUp: null, 326 | }; 327 | 328 | var defaultObj = { 329 | name: 'package', 330 | path: 'test/fixtures/fined', 331 | extensions: ['.json', '.js'], 332 | cwd: cwd, 333 | findUp: false, 334 | }; 335 | 336 | var expected = { 337 | path: path.resolve(cwd, 'test/fixtures/fined', 'package.json'), 338 | extension: '.json', 339 | }; 340 | 341 | var result = fined(pathObj, defaultObj); 342 | 343 | expect(result).toEqual(expected); 344 | done(); 345 | }); 346 | 347 | it('does not default when both arguments are complete', function (done) { 348 | var pathObj = { 349 | name: 'README', 350 | path: '.', 351 | extensions: ['.md', '.txt'], 352 | findUp: true, 353 | cwd: path.resolve(cwd, 'test/fixtures'), 354 | }; 355 | 356 | var defaultObj = { 357 | path: 'fixtures/fined', 358 | extensions: ['.json', '.js'], 359 | findUp: false, 360 | name: 'app', 361 | cwd: path.resolve(cwd, 'test'), 362 | }; 363 | 364 | var expected = { 365 | path: path.resolve(cwd, 'README.md'), 366 | extension: '.md', 367 | }; 368 | 369 | var result = fined(pathObj, defaultObj); 370 | 371 | expect(result).toEqual(expected); 372 | done(); 373 | }); 374 | 375 | it('defaults everything if 1st argument is null', function (done) { 376 | var pathObj = null; 377 | 378 | var defaultObj = { 379 | path: 'test/fixtures/fined', 380 | name: 'app', 381 | extensions: ['.json', '.js'], 382 | cwd: cwd, 383 | findUp: false, 384 | }; 385 | 386 | var expected = { 387 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 388 | extension: '.js', 389 | }; 390 | 391 | var result = fined(pathObj, defaultObj); 392 | 393 | expect(result).toEqual(expected); 394 | done(); 395 | }); 396 | 397 | it('ignores 2nd argument if it is null', function (done) { 398 | var pathObj = { 399 | path: 'test/fixtures/fined', 400 | name: 'app', 401 | extensions: ['.json', '.js'], 402 | cwd: cwd, 403 | findUp: false, 404 | }; 405 | 406 | var defaultObj = null; 407 | 408 | var expected = { 409 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 410 | extension: '.js', 411 | }; 412 | 413 | var result = fined(pathObj, defaultObj); 414 | 415 | expect(result).toEqual(expected); 416 | done(); 417 | }); 418 | 419 | it('ignores 2nd argument if it is invalid', function (done) { 420 | var pathObj = { 421 | path: 'test/fixtures/fined', 422 | name: 'app', 423 | extensions: ['.json', '.js'], 424 | cwd: cwd, 425 | findUp: false, 426 | }; 427 | 428 | var defaultObj = 123; 429 | 430 | var expected = { 431 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 432 | extension: '.js', 433 | }; 434 | 435 | var result = fined(pathObj, defaultObj); 436 | 437 | expect(result).toEqual(expected); 438 | done(); 439 | }); 440 | }); 441 | 442 | describe('Properties: `path`', function () { 443 | it('defaults `path` when it is null', function (done) { 444 | var pathObj = { 445 | path: null, 446 | }; 447 | 448 | var defaultObj = { 449 | path: 'fixtures/fined', 450 | extensions: ['.json', '.js'], 451 | findUp: false, 452 | name: 'app', 453 | cwd: path.resolve(cwd, 'test'), 454 | }; 455 | 456 | var expected = { 457 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 458 | extension: '.js', 459 | }; 460 | 461 | var result = fined(pathObj, defaultObj); 462 | 463 | expect(result).toEqual(expected); 464 | done(); 465 | }); 466 | 467 | it('returns null when `path` is null after defaulting', function (done) { 468 | var pathObj = { 469 | path: null, 470 | }; 471 | 472 | var defaultObj = { 473 | path: null, 474 | extensions: ['.json', '.js'], 475 | findUp: false, 476 | name: 'app', 477 | cwd: path.resolve(cwd, 'test'), 478 | }; 479 | 480 | var expected = null; 481 | 482 | var result = fined(pathObj, defaultObj); 483 | 484 | expect(result).toEqual(expected); 485 | done(); 486 | }); 487 | 488 | it('resolves to cwd + name + extension when `path` is an empty string', function (done) { 489 | var pathObj = { 490 | path: '', 491 | }; 492 | 493 | var defaultObj = { 494 | extensions: ['.json', '.js'], 495 | findUp: false, 496 | name: 'package', 497 | cwd: cwd, 498 | }; 499 | 500 | var expected = { 501 | path: path.resolve(cwd, 'package.json'), 502 | extension: '.json', 503 | }; 504 | 505 | var result = fined(pathObj, defaultObj); 506 | 507 | expect(result).toEqual(expected); 508 | done(); 509 | }); 510 | 511 | it('resolves to cwd when `path` and `name` are empty strings', function (done) { 512 | var pathObj = { 513 | path: '', 514 | name: '', 515 | }; 516 | 517 | var defaultObj = { 518 | extensions: [''], 519 | findUp: false, 520 | cwd: cwd, 521 | }; 522 | 523 | var expected = { 524 | path: path.resolve(cwd), 525 | extension: '', 526 | }; 527 | 528 | var result = fined(pathObj, defaultObj); 529 | 530 | expect(result).toEqual(expected); 531 | done(); 532 | }); 533 | 534 | it('returns null when `path` is an invalid type', function (done) { 535 | var pathObj = { 536 | path: function noop() {}, 537 | }; 538 | 539 | var defaultObj = { 540 | extensions: ['.js', '.json'], 541 | findUp: false, 542 | name: 'app', 543 | cwd: cwd, 544 | }; 545 | 546 | var expected = null; 547 | 548 | var result = fined(pathObj, defaultObj); 549 | 550 | expect(result).toEqual(expected); 551 | done(); 552 | }); 553 | 554 | it('resolves properly when `path` is a String object', function (done) { 555 | var pathObj = { 556 | path: new String('test/fixtures/fined'), 557 | }; 558 | 559 | var defaultObj = { 560 | extensions: ['.js', '.json'], 561 | findUp: false, 562 | name: 'app', 563 | cwd: cwd, 564 | }; 565 | 566 | var expected = { 567 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 568 | extension: '.js', 569 | }; 570 | 571 | var result = fined(pathObj, defaultObj); 572 | 573 | expect(result).toEqual(expected); 574 | 575 | var pathObj2 = { 576 | path: new String('test/fixtures/fined'), 577 | }; 578 | 579 | var defaultObj2 = { 580 | extensions: ['', '.js', '.json'], 581 | findUp: false, 582 | name: null, 583 | cwd: cwd, 584 | }; 585 | 586 | var expected2 = { 587 | path: path.resolve(cwd, 'test/fixtures/fined'), 588 | extension: '', 589 | }; 590 | 591 | var result2 = fined(pathObj2, defaultObj2); 592 | 593 | expect(result2).toEqual(expected2); 594 | done(); 595 | }); 596 | 597 | it('resolves `~` to homedir', function (done) { 598 | // ~ 599 | var pathObj = { 600 | path: '~', 601 | }; 602 | 603 | var defaultObj = { 604 | extensions: [userHomeFile.ext], 605 | findUp: false, 606 | name: userHomeFile.name, 607 | cwd: cwd, 608 | }; 609 | 610 | var expected = { 611 | path: userHomeFile.path, 612 | extension: userHomeFile.ext, 613 | }; 614 | 615 | var result = fined(pathObj, defaultObj); 616 | 617 | expect(result).toEqual(expected); 618 | 619 | // ~/xxx 620 | var pathObj2 = { 621 | path: '~/' + userHomeFile.name, 622 | }; 623 | 624 | var defaultObj2 = { 625 | extensions: [userHomeFile.ext], 626 | findUp: false, 627 | name: null, 628 | cwd: cwd, 629 | }; 630 | 631 | var expected2 = { 632 | path: userHomeFile.path, 633 | extension: userHomeFile.ext, 634 | }; 635 | 636 | var result2 = fined(pathObj2, defaultObj2); 637 | 638 | expect(result2).toEqual(expected2); 639 | 640 | // ~xxx 641 | var pathObj3 = { 642 | path: '~' + userHomeFile.name, 643 | }; 644 | 645 | var defaultObj3 = { 646 | extensions: [userHomeFile.ext], 647 | findUp: false, 648 | name: null, 649 | cwd: cwd, 650 | }; 651 | 652 | var expected3 = { 653 | path: userHomeFile.path, 654 | extension: userHomeFile.ext, 655 | }; 656 | 657 | var result3 = fined(pathObj3, defaultObj3); 658 | 659 | expect(result3).toEqual(expected3); 660 | done(); 661 | }); 662 | 663 | it('resolves `~+` to process.cwd()', function (done) { 664 | // ~+ 665 | var pathObj = { 666 | path: '~+', 667 | }; 668 | 669 | var defaultObj = { 670 | extensions: ['.json'], 671 | findUp: false, 672 | name: 'package', 673 | cwd: path.resolve(cwd, 'test/fixtures/fined'), 674 | }; 675 | 676 | var expected = { 677 | path: path.resolve(cwd, 'package.json'), 678 | extension: '.json', 679 | }; 680 | 681 | var result = fined(pathObj, defaultObj); 682 | 683 | expect(result).toEqual(expected); 684 | 685 | // ~+/xxx 686 | var pathObj2 = { 687 | path: '~+/package', 688 | }; 689 | 690 | var defaultObj2 = { 691 | extensions: ['.json'], 692 | findUp: false, 693 | name: '', 694 | cwd: path.resolve(cwd, 'test/fixtures/fined'), 695 | }; 696 | 697 | var expected2 = { 698 | path: path.resolve(cwd, 'package.json'), 699 | extension: '.json', 700 | }; 701 | 702 | var result2 = fined(pathObj2, defaultObj2); 703 | 704 | expect(result2).toEqual(expected2); 705 | 706 | // ~+xxx 707 | var pathObj3 = { 708 | path: '~+package', 709 | }; 710 | 711 | var defaultObj3 = { 712 | extensions: ['.json'], 713 | findUp: false, 714 | name: '', 715 | cwd: path.resolve(cwd, 'test/fixtures/fined'), 716 | }; 717 | 718 | var expected3 = { 719 | path: path.resolve(cwd, 'package.json'), 720 | extension: '.json', 721 | }; 722 | 723 | var result3 = fined(pathObj3, defaultObj3); 724 | 725 | expect(result3).toEqual(expected3); 726 | done(); 727 | }); 728 | 729 | it('ignores `cwd` when `path` is absolute', function (done) { 730 | var pathObj = { 731 | path: cwd, 732 | cwd: path.resolve(cwd, 'test/fixtures/fined'), 733 | }; 734 | 735 | var defaultObj = { 736 | extensions: ['.json'], 737 | findUp: false, 738 | name: 'package', 739 | }; 740 | 741 | var expected = { 742 | path: path.resolve(cwd, 'package.json'), 743 | extension: '.json', 744 | }; 745 | 746 | var result = fined(pathObj, defaultObj); 747 | 748 | expect(result).toEqual(expected); 749 | done(); 750 | }); 751 | 752 | it('ignores `cwd` when `path` has a drive letter (Windows only)', function (done) { 753 | if (!isWindows) { 754 | this.skip(); 755 | return; 756 | } 757 | 758 | var winDrive = cwd.slice(0, 2); 759 | 760 | var pathObj = { 761 | path: winDrive + 'test\\fixtures\\fined', 762 | }; 763 | 764 | var defaultObj = { 765 | name: 'app', 766 | findUp: false, 767 | extensions: ['.js'], 768 | }; 769 | 770 | var expected = { 771 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 772 | extension: '.js', 773 | }; 774 | 775 | var result = fined(pathObj, defaultObj); 776 | 777 | expect(result).toEqual(expected); 778 | done(); 779 | }); 780 | }); 781 | 782 | describe('Properties: `name`', function () { 783 | it('ignores `name` when null', function (done) { 784 | var pathObj = { 785 | name: null, 786 | }; 787 | 788 | var defaultObj = { 789 | path: 'test/fixtures/fined', 790 | extensions: [''], 791 | cwd: cwd, 792 | }; 793 | 794 | var expected = { 795 | path: path.resolve(cwd, 'test/fixtures/fined'), 796 | extension: '', 797 | }; 798 | 799 | var result = fined(pathObj, defaultObj); 800 | 801 | expect(result).toEqual(expected); 802 | done(); 803 | }); 804 | 805 | it('ignores `name` when it is an empty string', function (done) { 806 | var pathObj = { 807 | name: '', 808 | }; 809 | 810 | var defaultObj = { 811 | path: 'test/fixtures/fined', 812 | extensions: [''], 813 | cwd: cwd, 814 | }; 815 | 816 | var expected = { 817 | path: path.resolve(cwd, 'test/fixtures/fined'), 818 | extension: '', 819 | }; 820 | 821 | var result = fined(pathObj, defaultObj); 822 | 823 | expect(result).toEqual(expected); 824 | done(); 825 | }); 826 | 827 | it('ignores `name` when it is an invalid type', function (done) { 828 | var pathObj = { 829 | name: 123, 830 | }; 831 | 832 | var defaultObj = { 833 | path: 'test/fixtures/fined', 834 | extensions: [''], 835 | cwd: cwd, 836 | }; 837 | 838 | var expected = { 839 | path: path.resolve(cwd, 'test/fixtures/fined'), 840 | extension: '', 841 | }; 842 | 843 | var result = fined(pathObj, defaultObj); 844 | 845 | expect(result).toEqual(expected); 846 | done(); 847 | }); 848 | 849 | it('searches for file with `name` when it is a String object', function (done) { 850 | var pathObj = { 851 | name: new String('app'), 852 | }; 853 | 854 | var defaultObj = { 855 | path: 'test/fixtures/fined', 856 | extensions: ['.js'], 857 | cwd: cwd, 858 | }; 859 | 860 | var expected = { 861 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 862 | extension: '.js', 863 | }; 864 | 865 | var result = fined(pathObj, defaultObj); 866 | 867 | expect(result).toEqual(expected); 868 | 869 | var pathObj2 = { 870 | name: new String('package'), 871 | }; 872 | 873 | var defaultObj2 = { 874 | path: '', 875 | extensions: ['.json'], 876 | cwd: cwd, 877 | }; 878 | 879 | var expected2 = { 880 | path: path.resolve(cwd, 'package.json'), 881 | extension: '.json', 882 | }; 883 | 884 | var result2 = fined(pathObj2, defaultObj2); 885 | 886 | expect(result2).toEqual(expected2); 887 | done(); 888 | }); 889 | 890 | it('resolves `name` even when it is a directory', function (done) { 891 | var pathObj = { 892 | name: 'fined', 893 | }; 894 | 895 | var defaultObj = { 896 | path: 'test/fixtures', 897 | extensions: [''], 898 | cwd: cwd, 899 | }; 900 | 901 | var expected = { 902 | path: path.resolve(cwd, 'test/fixtures', 'fined'), 903 | extension: '', 904 | }; 905 | 906 | var result = fined(pathObj, defaultObj); 907 | 908 | expect(result).toEqual(expected); 909 | done(); 910 | }); 911 | 912 | it('resolves `name` when it is an absolute path and `path` is empty', function (done) { 913 | var pathObj = { 914 | name: path.resolve(cwd, 'test/fixtures/fined/app'), 915 | }; 916 | 917 | var defaultObj = { 918 | path: '', 919 | extensions: ['.js'], 920 | cwd: cwd, 921 | }; 922 | 923 | var expected = { 924 | path: path.resolve(cwd, 'test/fixtures/fined/app.js'), 925 | extension: '.js', 926 | }; 927 | 928 | var result = fined(pathObj, defaultObj); 929 | 930 | expect(result).toEqual(expected); 931 | 932 | var pathObj2 = { 933 | name: path.join(userHomeFile.dir, userHomeFile.name), 934 | }; 935 | 936 | var defaultObj2 = { 937 | path: '', 938 | extensions: [userHomeFile.ext], 939 | cwd: cwd, 940 | }; 941 | 942 | var expected2 = { 943 | path: userHomeFile.path, 944 | extension: userHomeFile.ext, 945 | }; 946 | 947 | var result2 = fined(pathObj2, defaultObj2); 948 | 949 | expect(result2).toEqual(expected2); 950 | done(); 951 | }); 952 | 953 | it('returns null when `name` is an absolute path but `path` is not empty', function (done) { 954 | var pathObj = { 955 | name: path.resolve(cwd, 'test/fixtures/fined/app'), 956 | path: 'test/fixtures/fined', 957 | }; 958 | 959 | var defaultObj = { 960 | extensions: ['.js'], 961 | cwd: cwd, 962 | }; 963 | 964 | var expected = null; 965 | 966 | var result = fined(pathObj, defaultObj); 967 | 968 | expect(result).toEqual(expected); 969 | done(); 970 | }); 971 | 972 | it('will not expand `~` as part of `name`', function (done) { 973 | var pathObj = { 974 | name: '~/' + userHomeFile.name, 975 | }; 976 | 977 | var defaultObj = { 978 | path: '', 979 | extensions: [userHomeFile.ext], 980 | cwd: cwd, 981 | }; 982 | 983 | var expected = null; 984 | 985 | var result = fined(pathObj, defaultObj); 986 | 987 | expect(result).toEqual(expected); 988 | done(); 989 | }); 990 | }); 991 | 992 | describe('Properties: `extensions`', function () { 993 | it('resolves to the extension if it is a string', function (done) { 994 | var pathObj = { 995 | extensions: '.js', 996 | }; 997 | 998 | var defaultObj = { 999 | path: 'test/fixtures/fined', 1000 | name: 'app', 1001 | cwd: cwd, 1002 | }; 1003 | 1004 | var expected = { 1005 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 1006 | extension: '.js', 1007 | }; 1008 | 1009 | var result = fined(pathObj, defaultObj); 1010 | 1011 | expect(result).toEqual(expected); 1012 | done(); 1013 | }); 1014 | 1015 | it('resolves to the first found extension if it is an array', function (done) { 1016 | var pathObj = { 1017 | extensions: ['.json', '.txt', '.js'], 1018 | }; 1019 | 1020 | var defaultObj = { 1021 | path: 'test/fixtures/fined', 1022 | name: 'app', 1023 | cwd: cwd, 1024 | }; 1025 | 1026 | var expected = { 1027 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 1028 | extension: '.js', 1029 | }; 1030 | 1031 | var result = fined(pathObj, defaultObj); 1032 | 1033 | expect(result).toEqual(expected); 1034 | done(); 1035 | }); 1036 | 1037 | it('resolves to the first found extension if it is an object', function (done) { 1038 | var pathObj = { 1039 | extensions: { 1040 | '.json': 1, 1041 | '.js': 2, 1042 | '.txt': 3, 1043 | '.yml': 4, 1044 | }, 1045 | }; 1046 | 1047 | var defaultObj = { 1048 | path: 'test/fixtures/fined', 1049 | name: 'app', 1050 | cwd: cwd, 1051 | }; 1052 | 1053 | var expected = { 1054 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 1055 | extension: { '.js': 2 }, 1056 | }; 1057 | 1058 | var result = fined(pathObj, defaultObj); 1059 | 1060 | expect(result).toEqual(expected); 1061 | done(); 1062 | }); 1063 | 1064 | it('resolves to the first found extension if multiple match', function (done) { 1065 | var pathObj = { 1066 | extensions: ['.json', '.js'], 1067 | }; 1068 | 1069 | var defaultObj = { 1070 | path: 'test/fixtures/fined', 1071 | name: 'appfile', 1072 | cwd: cwd, 1073 | }; 1074 | 1075 | var expected = { 1076 | path: path.resolve(cwd, 'test/fixtures/fined', 'appfile.json'), 1077 | extension: '.json', 1078 | }; 1079 | 1080 | var result = fined(pathObj, defaultObj); 1081 | 1082 | expect(result).toEqual(expected); 1083 | 1084 | var pathObj2 = { 1085 | extensions: ['.js', '.json'], 1086 | }; 1087 | 1088 | var defaultObj2 = { 1089 | path: 'test/fixtures/fined', 1090 | name: 'appfile', 1091 | cwd: cwd, 1092 | }; 1093 | 1094 | var expected2 = { 1095 | path: path.resolve(cwd, 'test/fixtures/fined', 'appfile.js'), 1096 | extension: '.js', 1097 | }; 1098 | 1099 | var result2 = fined(pathObj2, defaultObj2); 1100 | 1101 | expect(result2).toEqual(expected2); 1102 | done(); 1103 | }); 1104 | 1105 | it('treats a null value as an empty array', function (done) { 1106 | var pathObj = { 1107 | extensions: null, 1108 | }; 1109 | 1110 | var defaultObj = { 1111 | path: 'test/fixtures', 1112 | name: 'fined', 1113 | cwd: cwd, 1114 | }; 1115 | 1116 | var expected = { 1117 | path: path.resolve(cwd, 'test/fixtures', 'fined'), 1118 | extension: '', 1119 | }; 1120 | 1121 | var result = fined(pathObj, defaultObj); 1122 | 1123 | expect(result).toEqual(expected); 1124 | done(); 1125 | }); 1126 | 1127 | it('treats an empty string value as an empty array', function (done) { 1128 | var pathObj = { 1129 | extensions: '', 1130 | }; 1131 | 1132 | var defaultObj = { 1133 | path: 'test/fixtures', 1134 | name: 'fined', 1135 | cwd: cwd, 1136 | }; 1137 | 1138 | var expected = { 1139 | path: path.resolve(cwd, 'test/fixtures', 'fined'), 1140 | extension: '', 1141 | }; 1142 | 1143 | var result = fined(pathObj, defaultObj); 1144 | 1145 | expect(result).toEqual(expected); 1146 | done(); 1147 | }); 1148 | 1149 | it('treats an empty array as an empty array', function (done) { 1150 | var pathObj = { 1151 | extensions: [], 1152 | }; 1153 | 1154 | var defaultObj = { 1155 | path: 'test/fixtures', 1156 | name: 'fined', 1157 | cwd: cwd, 1158 | }; 1159 | 1160 | var expected = { 1161 | path: path.resolve(cwd, 'test/fixtures', 'fined'), 1162 | extension: '', 1163 | }; 1164 | 1165 | var result = fined(pathObj, defaultObj); 1166 | 1167 | expect(result).toEqual(expected); 1168 | done(); 1169 | }); 1170 | 1171 | it('treats an empty object as an object with only key being an empty string', function (done) { 1172 | var pathObj = { 1173 | extensions: {}, 1174 | }; 1175 | 1176 | var defaultObj = { 1177 | path: 'test/fixtures', 1178 | name: 'fined', 1179 | cwd: cwd, 1180 | }; 1181 | 1182 | var expected = { 1183 | path: path.resolve(cwd, 'test/fixtures', 'fined'), 1184 | extension: { '': null }, 1185 | }; 1186 | 1187 | var result = fined(pathObj, defaultObj); 1188 | 1189 | expect(result).toEqual(expected); 1190 | done(); 1191 | }); 1192 | 1193 | it('treats an invalid type as an empty array', function (done) { 1194 | var pathObj = { 1195 | extensions: 123, 1196 | }; 1197 | 1198 | var defaultObj = { 1199 | path: 'test/fixtures', 1200 | name: 'fined', 1201 | cwd: cwd, 1202 | }; 1203 | 1204 | var expected = { 1205 | path: path.resolve(cwd, 'test/fixtures', 'fined'), 1206 | extension: '', 1207 | }; 1208 | 1209 | var result = fined(pathObj, defaultObj); 1210 | 1211 | expect(result).toEqual(expected); 1212 | done(); 1213 | }); 1214 | 1215 | it('supports String objects', function (done) { 1216 | var pathObj = { 1217 | extensions: [new String('.json'), new String('.js')], 1218 | }; 1219 | 1220 | var defaultObj = { 1221 | path: 'test/fixtures/fined', 1222 | name: 'app', 1223 | cwd: cwd, 1224 | }; 1225 | 1226 | var expected = { 1227 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 1228 | extension: new String('.js'), 1229 | }; 1230 | 1231 | var result = fined(pathObj, defaultObj); 1232 | 1233 | expect(result).toEqual(expected); 1234 | 1235 | var exts = {}; 1236 | exts[new String('.json')] = 1; 1237 | exts[new String('.js')] = 2; 1238 | 1239 | var pathObj2 = { 1240 | extensions: exts, 1241 | }; 1242 | 1243 | var defaultObj2 = { 1244 | path: 'test/fixtures/fined', 1245 | name: 'app', 1246 | cwd: cwd, 1247 | }; 1248 | 1249 | var expected2 = { 1250 | path: path.resolve(cwd, 'test/fixtures/fined', 'app.js'), 1251 | extension: { '.js': 2 }, 1252 | }; 1253 | 1254 | var result2 = fined(pathObj2, defaultObj2); 1255 | 1256 | expect(result2).toEqual(expected2); 1257 | done(); 1258 | }); 1259 | }); 1260 | 1261 | describe('Properties: `cwd`', function () { 1262 | it('can be absolute', function (done) { 1263 | var pathObj = { 1264 | cwd: path.resolve('.'), 1265 | }; 1266 | 1267 | var defaultObj = { 1268 | path: 'test/fixtures/fined', 1269 | name: 'appfile', 1270 | extensions: '.js', 1271 | }; 1272 | 1273 | var expected = { 1274 | path: path.resolve('.', 'test/fixtures/fined/appfile.js'), 1275 | extension: '.js', 1276 | }; 1277 | 1278 | var result = fined(pathObj, defaultObj); 1279 | 1280 | expect(result).toEqual(expected); 1281 | done(); 1282 | }); 1283 | 1284 | it('can be relative', function (done) { 1285 | var pathObj = { 1286 | cwd: '.', 1287 | }; 1288 | 1289 | var defaultObj = { 1290 | path: 'test/fixtures/fined', 1291 | name: 'appfile', 1292 | extensions: '.js', 1293 | }; 1294 | 1295 | var expected = { 1296 | path: path.resolve('.', 'test/fixtures/fined/appfile.js'), 1297 | extension: '.js', 1298 | }; 1299 | 1300 | var result = fined(pathObj, defaultObj); 1301 | 1302 | expect(result).toEqual(expected); 1303 | 1304 | var pathObj2 = { 1305 | cwd: 'test/fixtures', 1306 | }; 1307 | 1308 | var defaultObj2 = { 1309 | path: 'fined', 1310 | name: 'appfile', 1311 | extensions: '.js', 1312 | }; 1313 | 1314 | var expected2 = { 1315 | path: path.resolve('.', 'test/fixtures/fined/appfile.js'), 1316 | extension: '.js', 1317 | }; 1318 | 1319 | var result2 = fined(pathObj2, defaultObj2); 1320 | 1321 | expect(result2).toEqual(expected2); 1322 | done(); 1323 | }); 1324 | 1325 | it('treats a null value as `.`', function (done) { 1326 | var pathObj = { 1327 | cwd: null, 1328 | }; 1329 | 1330 | var defaultObj = { 1331 | path: 'test/fixtures/fined', 1332 | name: 'appfile', 1333 | extensions: '.js', 1334 | }; 1335 | 1336 | var expected = { 1337 | path: path.resolve('.', 'test/fixtures/fined/appfile.js'), 1338 | extension: '.js', 1339 | }; 1340 | 1341 | var result = fined(pathObj, defaultObj); 1342 | 1343 | expect(result).toEqual(expected); 1344 | done(); 1345 | }); 1346 | 1347 | it('treats an empty string as `.`', function (done) { 1348 | var pathObj = { 1349 | cwd: '', 1350 | }; 1351 | 1352 | var defaultObj = { 1353 | path: 'test/fixtures/fined', 1354 | name: 'appfile', 1355 | extensions: '.js', 1356 | }; 1357 | 1358 | var expected = { 1359 | path: path.resolve('.', 'test/fixtures/fined/appfile.js'), 1360 | extension: '.js', 1361 | }; 1362 | 1363 | var result = fined(pathObj, defaultObj); 1364 | 1365 | expect(result).toEqual(expected); 1366 | done(); 1367 | }); 1368 | 1369 | it('treats an invalid type as `.`', function (done) { 1370 | var pathObj = { 1371 | cwd: 123, 1372 | }; 1373 | 1374 | var defaultObj = { 1375 | path: 'test/fixtures/fined', 1376 | name: 'appfile', 1377 | extensions: '.js', 1378 | }; 1379 | 1380 | var expected = { 1381 | path: path.resolve('.', 'test/fixtures/fined/appfile.js'), 1382 | extension: '.js', 1383 | }; 1384 | 1385 | var result = fined(pathObj, defaultObj); 1386 | 1387 | expect(result).toEqual(expected); 1388 | done(); 1389 | }); 1390 | 1391 | it('supports String objects', function (done) { 1392 | var pathObj = { 1393 | cwd: new String(cwd), 1394 | }; 1395 | 1396 | var defaultObj = { 1397 | path: 'test/fixtures/fined', 1398 | name: 'appfile', 1399 | extensions: '.js', 1400 | }; 1401 | 1402 | var expected = { 1403 | path: path.resolve(cwd, 'test/fixtures/fined/appfile.js'), 1404 | extension: '.js', 1405 | }; 1406 | 1407 | var result = fined(pathObj, defaultObj); 1408 | 1409 | expect(result).toEqual(expected); 1410 | done(); 1411 | }); 1412 | 1413 | it('expands `~` to homedir', function (done) { 1414 | var pathObj = { 1415 | cwd: '~', 1416 | }; 1417 | 1418 | var defaultObj = { 1419 | path: '', 1420 | name: userHomeFile.name, 1421 | extensions: userHomeFile.ext, 1422 | }; 1423 | 1424 | var expected = { 1425 | path: userHomeFile.path, 1426 | extension: userHomeFile.ext, 1427 | }; 1428 | 1429 | var result = fined(pathObj, defaultObj); 1430 | 1431 | expect(result).toEqual(expected); 1432 | done(); 1433 | }); 1434 | }); 1435 | 1436 | describe('Properties: `findUp`', function () { 1437 | it('finds a file up in the tree', function (done) { 1438 | var pathObj = { 1439 | path: '', 1440 | findUp: true, 1441 | }; 1442 | 1443 | var defaultObj = { 1444 | name: 'README', 1445 | extensions: ['.md'], 1446 | cwd: 'test/fixtures/fined', 1447 | }; 1448 | 1449 | var expected = { 1450 | path: path.resolve(cwd, 'README.md'), 1451 | extension: '.md', 1452 | }; 1453 | 1454 | var result = fined(pathObj, defaultObj); 1455 | 1456 | expect(result).toEqual(expected); 1457 | done(); 1458 | }); 1459 | 1460 | it('finds a directory up in the tree', function (done) { 1461 | var pathObj = { 1462 | path: '', 1463 | findUp: true, 1464 | }; 1465 | 1466 | var defaultObj = { 1467 | name: 'test', 1468 | extensions: ['.md', ''], 1469 | cwd: 'fixtures/fined', 1470 | }; 1471 | 1472 | var expected = { 1473 | path: path.resolve(cwd, 'test'), 1474 | extension: '', 1475 | }; 1476 | 1477 | var result = fined(pathObj, defaultObj); 1478 | 1479 | expect(result).toEqual(expected); 1480 | done(); 1481 | }); 1482 | 1483 | it('does not search up the tree if file exists in cwd', function (done) { 1484 | var pathObj = { 1485 | path: '', 1486 | extensions: '.json', 1487 | findUp: true, 1488 | }; 1489 | 1490 | var defaultObj = { 1491 | name: 'package', 1492 | cwd: 'test/fixtures/fined', 1493 | }; 1494 | 1495 | var expected = { 1496 | path: path.resolve(cwd, 'test/fixtures/fined', 'package.json'), 1497 | extension: '.json', 1498 | }; 1499 | 1500 | var result = fined(pathObj, defaultObj); 1501 | 1502 | expect(result).toEqual(expected); 1503 | done(); 1504 | }); 1505 | 1506 | it('does not search up the tree if `path` is absolute', function (done) { 1507 | var pathObj = { 1508 | findUp: true, 1509 | path: path.resolve(cwd, 'test'), 1510 | extensions: '.md', 1511 | }; 1512 | 1513 | var defaultObj = { 1514 | name: 'README', 1515 | cwd: path.resolve(cwd, 'test'), 1516 | }; 1517 | 1518 | var expected = null; 1519 | 1520 | var result = fined(pathObj, defaultObj); 1521 | 1522 | expect(result).toEqual(expected); 1523 | done(); 1524 | }); 1525 | 1526 | it('does not search up the tree if `cwd` has a drive letter (Windows only)', function (done) { 1527 | if (!isWindows) { 1528 | this.skip(); 1529 | return; 1530 | } 1531 | 1532 | var winDrive = cwd.slice(0, 2); 1533 | 1534 | var pathObj = { 1535 | findUp: true, 1536 | name: 'package', 1537 | path: '', 1538 | cwd: winDrive + 'test\\fixtures', 1539 | extensions: '.json', 1540 | }; 1541 | 1542 | var defaultObj = {}; 1543 | 1544 | var expected = { 1545 | path: path.resolve(cwd, 'package.json'), 1546 | extension: '.json', 1547 | }; 1548 | 1549 | var result = fined(pathObj, defaultObj); 1550 | 1551 | expect(result).toEqual(expected); 1552 | done(); 1553 | }); 1554 | 1555 | it('does not search up the tree any more if file with another extension candidate exists', function (done) { 1556 | var pathObj = { 1557 | findUp: true, 1558 | path: '.', 1559 | extensions: ['.js', '.json'], 1560 | }; 1561 | 1562 | var defaultObj = { 1563 | name: 'index', 1564 | cwd: path.resolve(cwd, 'test/fixtures/fined'), 1565 | }; 1566 | 1567 | var expected = { 1568 | path: path.resolve(cwd, 'test/fixtures/fined/index.json'), 1569 | extension: '.json', 1570 | }; 1571 | 1572 | var result = fined(pathObj, defaultObj); 1573 | 1574 | expect(result).toEqual(expected); 1575 | done(); 1576 | }); 1577 | }); 1578 | 1579 | describe('Symbolic links', function () { 1580 | it('returns symlink path when found link points to a file', function (done) { 1581 | var pathObj = { 1582 | path: '.', 1583 | name: symlinkedFiles[0].name, 1584 | extensions: [symlinkedFiles[0].ext], 1585 | cwd: symlinkedFiles[0].dir, 1586 | }; 1587 | 1588 | var expected = { 1589 | path: symlinkedFiles[0].path, 1590 | extension: symlinkedFiles[0].ext, 1591 | }; 1592 | 1593 | var result = fined(pathObj); 1594 | 1595 | expect(result).toEqual(expected); 1596 | 1597 | var pathObj2 = { 1598 | path: '.', 1599 | name: symlinkedFiles[1].name, 1600 | extensions: [symlinkedFiles[1].ext], 1601 | cwd: symlinkedFiles[1].dir, 1602 | }; 1603 | 1604 | var expected2 = { 1605 | path: symlinkedFiles[1].path, 1606 | extension: symlinkedFiles[1].ext, 1607 | }; 1608 | 1609 | var result2 = fined(pathObj2); 1610 | 1611 | expect(result2).toEqual(expected2); 1612 | done(); 1613 | }); 1614 | 1615 | it('returns symlink path when found link points to a directory', function (done) { 1616 | var pathObj = { 1617 | path: '.', 1618 | name: symlinkedFiles[4].name, 1619 | extensions: [symlinkedFiles[4].ext], 1620 | cwd: symlinkedFiles[4].dir, 1621 | }; 1622 | 1623 | var expected = { 1624 | path: symlinkedFiles[4].path, 1625 | extension: symlinkedFiles[4].ext, 1626 | }; 1627 | 1628 | var result = fined(pathObj); 1629 | 1630 | expect(result).toEqual(expected); 1631 | 1632 | var pathObj2 = { 1633 | path: '.', 1634 | name: symlinkedFiles[5].name, 1635 | extensions: [symlinkedFiles[5].ext], 1636 | cwd: symlinkedFiles[5].dir, 1637 | }; 1638 | 1639 | var expected2 = { 1640 | path: symlinkedFiles[5].path, 1641 | extension: symlinkedFiles[5].ext, 1642 | }; 1643 | 1644 | var result2 = fined(pathObj2); 1645 | 1646 | expect(result2).toEqual(expected2); 1647 | done(); 1648 | }); 1649 | 1650 | it('returns null when found link is an invalid symlink', function (done) { 1651 | var pathObj = { 1652 | path: '.', 1653 | name: symlinkedFiles[2].name, 1654 | extensions: [symlinkedFiles[2].ext], 1655 | cwd: symlinkedFiles[2].dir, 1656 | }; 1657 | 1658 | var expected = null; 1659 | 1660 | var result = fined(pathObj); 1661 | 1662 | expect(result).toEqual(expected); 1663 | 1664 | var pathObj2 = { 1665 | path: '.', 1666 | name: symlinkedFiles[3].name, 1667 | extensions: [symlinkedFiles[3].ext], 1668 | cwd: symlinkedFiles[3].dir, 1669 | }; 1670 | 1671 | var expected2 = null; 1672 | 1673 | var result2 = fined(pathObj2); 1674 | 1675 | expect(result2).toEqual(expected2); 1676 | done(); 1677 | }); 1678 | 1679 | it('returns symlink path during findUp when symlink points to a file', function (done) { 1680 | var pathObj = { 1681 | path: path.basename(symlinkedFiles[0].dir), 1682 | name: symlinkedFiles[0].name, 1683 | extensions: [symlinkedFiles[0].ext], 1684 | cwd: symlinkedFiles[0].dir, 1685 | findUp: true, 1686 | }; 1687 | 1688 | var expected = { 1689 | path: symlinkedFiles[0].path, 1690 | extension: symlinkedFiles[0].ext, 1691 | }; 1692 | 1693 | var result = fined(pathObj); 1694 | 1695 | expect(result).toEqual(expected); 1696 | 1697 | var pathObj2 = { 1698 | path: path.basename(symlinkedFiles[1].dir), 1699 | name: symlinkedFiles[1].name, 1700 | extensions: [symlinkedFiles[1].ext], 1701 | cwd: symlinkedFiles[1].dir, 1702 | findUp: true, 1703 | }; 1704 | 1705 | var expected2 = { 1706 | path: symlinkedFiles[1].path, 1707 | extension: symlinkedFiles[1].ext, 1708 | }; 1709 | 1710 | var result2 = fined(pathObj2); 1711 | 1712 | expect(result2).toEqual(expected2); 1713 | done(); 1714 | }); 1715 | 1716 | it('returns symlink path during findUp when symlink points to a directory', function (done) { 1717 | var pathObj = { 1718 | path: path.basename(symlinkedFiles[4].dir), 1719 | name: symlinkedFiles[4].name, 1720 | extensions: [symlinkedFiles[4].ext], 1721 | cwd: symlinkedFiles[4].dir, 1722 | findUp: true, 1723 | }; 1724 | 1725 | var expected = { 1726 | path: symlinkedFiles[4].path, 1727 | extension: symlinkedFiles[4].ext, 1728 | }; 1729 | 1730 | var result = fined(pathObj); 1731 | 1732 | expect(result).toEqual(expected); 1733 | 1734 | var pathObj2 = { 1735 | path: path.basename(symlinkedFiles[5].dir), 1736 | name: symlinkedFiles[5].name, 1737 | extensions: [symlinkedFiles[5].ext], 1738 | cwd: symlinkedFiles[5].dir, 1739 | findUp: true, 1740 | }; 1741 | 1742 | var expected2 = { 1743 | path: symlinkedFiles[5].path, 1744 | extension: symlinkedFiles[5].ext, 1745 | }; 1746 | 1747 | var result2 = fined(pathObj2); 1748 | 1749 | expect(result2).toEqual(expected2); 1750 | done(); 1751 | }); 1752 | 1753 | it('returns null during findUp when symlink is invalid', function (done) { 1754 | var pathObj = { 1755 | path: path.basename(symlinkedFiles[2].dir), 1756 | name: symlinkedFiles[2].name, 1757 | extensions: [symlinkedFiles[2].ext], 1758 | cwd: symlinkedFiles[2].dir, 1759 | findUp: true, 1760 | }; 1761 | 1762 | var expected = null; 1763 | 1764 | var result = fined(pathObj); 1765 | 1766 | expect(result).toEqual(expected); 1767 | 1768 | var pathObj2 = { 1769 | path: path.basename(symlinkedFiles[3].dir), 1770 | name: symlinkedFiles[3].name, 1771 | extensions: [symlinkedFiles[3].ext], 1772 | cwd: symlinkedFiles[3].dir, 1773 | findUp: true, 1774 | }; 1775 | 1776 | var expected2 = null; 1777 | 1778 | var result2 = fined(pathObj2); 1779 | 1780 | expect(result2).toEqual(expected2); 1781 | done(); 1782 | }); 1783 | }); 1784 | -------------------------------------------------------------------------------- /test/utils/create-symlinks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | 6 | var dir = path.resolve(__dirname, '../fixtures/fined'); 7 | var basedir = path.resolve(__dirname, '../../'); 8 | 9 | var symlinkedFiles = [0, 1, 2, 3, 4, 5].map(function (v, j) { 10 | return path.resolve(dir, 'symlink' + j + '.json'); 11 | }); 12 | 13 | if (fs.existsSync(symlinkedFiles[0])) { 14 | for (var i0 = symlinkedFiles.length - 1; i0 >= 0; i0--) { 15 | fs.unlinkSync(symlinkedFiles[i0]); 16 | } 17 | } 18 | 19 | var linkedFiles = [ 20 | { path: 'package.json', type: 'file' }, 21 | { path: 'xxxx', type: 'file' }, 22 | { path: 'test/', type: 'dir' }, 23 | ]; 24 | for (var i = 0, n = linkedFiles.length; i < n; i++) { 25 | var file = linkedFiles[i]; 26 | fs.symlinkSync( 27 | path.resolve(basedir, file.path), 28 | symlinkedFiles[i * 2], 29 | file.type 30 | ); 31 | fs.symlinkSync(symlinkedFiles[i * 2], symlinkedFiles[i * 2 + 1], file.type); 32 | } 33 | 34 | module.exports = symlinkedFiles.map(function (pth) { 35 | var ext = path.extname(pth); 36 | return { 37 | path: pth, 38 | dir: path.dirname(pth), 39 | ext: ext, 40 | name: path.basename(pth, ext), 41 | }; 42 | }); 43 | -------------------------------------------------------------------------------- /test/utils/get-userhome-file.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var expandTilde = require('expand-tilde'); 6 | 7 | var userHomeDir = expandTilde('~'); 8 | var userHomeFiles = fs.readdirSync(userHomeDir); 9 | 10 | var userHomeFilePath, userHomeFileExt, userHomeFileName, userHomeFileDir; 11 | 12 | if (userHomeFiles.length > 0) { 13 | var filePath = path.resolve(userHomeDir, userHomeFiles[0]); 14 | userHomeFilePath = filePath; 15 | userHomeFileDir = path.dirname(userHomeFilePath); 16 | userHomeFileExt = path.extname(userHomeFilePath); 17 | userHomeFileName = path.basename(userHomeFilePath, userHomeFileExt); 18 | } 19 | 20 | module.exports = { 21 | path: userHomeFilePath, 22 | name: userHomeFileName, 23 | ext: userHomeFileExt, 24 | dir: userHomeFileDir, 25 | }; 26 | --------------------------------------------------------------------------------