├── test ├── fixtures │ ├── a │ │ ├── b │ │ │ ├── a.md │ │ │ ├── ONE.js │ │ │ ├── c │ │ │ │ ├── ONE.txt │ │ │ │ ├── two.txt │ │ │ │ ├── c.txt │ │ │ │ ├── d │ │ │ │ │ ├── d.txt │ │ │ │ │ ├── one.txt │ │ │ │ │ ├── e │ │ │ │ │ │ ├── e.txt │ │ │ │ │ │ ├── f │ │ │ │ │ │ │ ├── f.txt │ │ │ │ │ │ │ ├── file.txt │ │ │ │ │ │ │ ├── g │ │ │ │ │ │ │ │ ├── file.txt │ │ │ │ │ │ │ │ ├── g.txt │ │ │ │ │ │ │ │ ├── h │ │ │ │ │ │ │ │ │ ├── h.txt │ │ │ │ │ │ │ │ │ ├── i │ │ │ │ │ │ │ │ │ │ ├── i.txt │ │ │ │ │ │ │ │ │ │ ├── file.txt │ │ │ │ │ │ │ │ │ │ ├── j │ │ │ │ │ │ │ │ │ │ │ ├── file.txt │ │ │ │ │ │ │ │ │ │ │ ├── j.txt │ │ │ │ │ │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ │ │ │ │ │ └── Mochafile.txt │ │ │ │ │ │ │ │ │ │ ├── package.json │ │ │ │ │ │ │ │ │ │ └── Mochafile.txt │ │ │ │ │ │ │ │ │ ├── file.txt │ │ │ │ │ │ │ │ │ ├── Mochafile.txt │ │ │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ │ │ ├── Mochafile.txt │ │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ │ ├── Mochafile.txt │ │ │ │ │ │ │ └── package.json │ │ │ │ │ │ ├── file.txt │ │ │ │ │ │ ├── Mochafile.txt │ │ │ │ │ │ └── package.json │ │ │ │ │ ├── file.txt │ │ │ │ │ ├── Mochafile.txt │ │ │ │ │ └── package.json │ │ │ │ ├── file.txt │ │ │ │ ├── Mochafile.txt │ │ │ │ └── package.json │ │ │ ├── one.txt │ │ │ ├── .config │ │ │ │ └── waldo │ │ │ ├── b.txt │ │ │ ├── file.txt │ │ │ ├── Mochafile.txt │ │ │ └── package.json │ │ ├── one.txt │ │ ├── a.txt │ │ ├── file.txt │ │ ├── Mochafile.txt │ │ └── package.json │ ├── file.txt │ ├── root.txt │ ├── Mochafile.txt │ └── package.json ├── support │ └── index.js └── test.js ├── .eslintignore ├── .npmrc ├── .prettierignore ├── .eslintrc ├── .gitattributes ├── .editorconfig ├── .github └── workflows │ ├── release.yml │ └── dev.yml ├── CHANGELOG.md ├── LICENSE ├── .gitignore ├── package.json ├── README.md └── index.js /test/fixtures/a/b/a.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/a/one.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/a/a.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/ONE.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/ONE.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/two.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/a/b/one.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | test/fixtures/ 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/.config/waldo: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/a/b/b.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/c.txt: -------------------------------------------------------------------------------- 1 | c 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/d.txt: -------------------------------------------------------------------------------- 1 | d 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/one.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/a/b/file.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/fixtures/a/file.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/fixtures/file.txt: -------------------------------------------------------------------------------- 1 | root 2 | -------------------------------------------------------------------------------- /test/fixtures/root.txt: -------------------------------------------------------------------------------- 1 | root 2 | -------------------------------------------------------------------------------- /test/fixtures/Mochafile.txt: -------------------------------------------------------------------------------- 1 | root 2 | -------------------------------------------------------------------------------- /test/fixtures/a/Mochafile.txt: -------------------------------------------------------------------------------- 1 | a 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/Mochafile.txt: -------------------------------------------------------------------------------- 1 | b 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/e.txt: -------------------------------------------------------------------------------- 1 | e 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/f.txt: -------------------------------------------------------------------------------- 1 | f 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/file.txt: -------------------------------------------------------------------------------- 1 | e 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/file.txt: -------------------------------------------------------------------------------- 1 | d 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/file.txt: -------------------------------------------------------------------------------- 1 | c 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/Mochafile.txt: -------------------------------------------------------------------------------- 1 | c 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/Mochafile.txt: -------------------------------------------------------------------------------- 1 | d 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/Mochafile.txt: -------------------------------------------------------------------------------- 1 | e 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/file.txt: -------------------------------------------------------------------------------- 1 | f 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/file.txt: -------------------------------------------------------------------------------- 1 | g 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/g.txt: -------------------------------------------------------------------------------- 1 | g 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/h/h.txt: -------------------------------------------------------------------------------- 1 | h 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/h/i/i.txt: -------------------------------------------------------------------------------- 1 | i 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | audit=false 3 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/Mochafile.txt: -------------------------------------------------------------------------------- 1 | f 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/Mochafile.txt: -------------------------------------------------------------------------------- 1 | g 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/h/file.txt: -------------------------------------------------------------------------------- 1 | h 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/h/i/file.txt: -------------------------------------------------------------------------------- 1 | i 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/h/i/j/file.txt: -------------------------------------------------------------------------------- 1 | j 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/h/i/j/j.txt: -------------------------------------------------------------------------------- 1 | j 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/h/i/j/package.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/h/i/package.json: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/h/Mochafile.txt: -------------------------------------------------------------------------------- 1 | h 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/h/i/Mochafile.txt: -------------------------------------------------------------------------------- 1 | i 2 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/h/i/j/Mochafile.txt: -------------------------------------------------------------------------------- 1 | j 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | .nyc_output/ 3 | CHANGELOG.md 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "gulp", 3 | "rules": { 4 | "max-len": 0 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/a/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "a", 3 | "description": "package.json from a." 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "c", 3 | "description": "package.json from c." 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/a/b/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "b", 3 | "description": "package.json from b." 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "f", 3 | "description": "package.json from f." 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e", 3 | "description": "package.json from e." 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "d", 3 | "description": "package.json from d." 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/h/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "h", 3 | "description": "package.json from h." 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/d/e/f/g/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "g", 3 | "description": "package.json from g." 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fixtures", 3 | "description": "package.json from ./fixtures." 4 | } 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enforce Unix newlines 2 | *.* text eol=lf 3 | 4 | *.jpg binary 5 | *.gif binary 6 | *.png binary 7 | *.jpeg binary -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [5.0.0](https://www.github.com/gulpjs/findup-sync/compare/v4.0.0...v5.0.0) (2021-10-24) 4 | 5 | 6 | ### ⚠ BREAKING CHANGES 7 | 8 | * Normalize repository, dropping node <10.13 support (#52) 9 | 10 | ### Miscellaneous Chores 11 | 12 | * Normalize repository, dropping node <10.13 support ([#52](https://www.github.com/gulpjs/findup-sync/issues/52)) ([b62b918](https://www.github.com/gulpjs/findup-sync/commit/b62b918843898c6bd475ddf2a874138fb6aec0b9)) 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2019, 2021 Ben Alman , 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 | -------------------------------------------------------------------------------- /.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 | 66 | # Test results 67 | test.xunit 68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "findup-sync", 3 | "version": "5.0.0", 4 | "description": "Find the first file matching a given pattern in the current directory or the nearest ancestor directory.", 5 | "author": "Gulp Team (https://gulpjs.com/)", 6 | "contributors": [ 7 | "Ben Alman ", 8 | "Tyler Kellen ", 9 | "Jon Schlinkert ", 10 | "Blaine Bublitz " 11 | ], 12 | "repository": "gulpjs/findup-sync", 13 | "license": "MIT", 14 | "engines": { 15 | "node": ">= 10.13.0" 16 | }, 17 | "main": "index.js", 18 | "files": [ 19 | "index.js", 20 | "LICENSE" 21 | ], 22 | "scripts": { 23 | "lint": "eslint .", 24 | "pretest": "npm run lint", 25 | "test": "nyc mocha --async-only" 26 | }, 27 | "dependencies": { 28 | "detect-file": "^1.0.0", 29 | "is-glob": "^4.0.3", 30 | "micromatch": "^4.0.4", 31 | "resolve-dir": "^1.0.1" 32 | }, 33 | "devDependencies": { 34 | "eslint": "^7.21.0", 35 | "eslint-config-gulp": "^5.0.1", 36 | "eslint-plugin-node": "^11.1.0", 37 | "expect": "^27.3.1", 38 | "homedir-polyfill": "^1.0.3", 39 | "mocha": "^6.1.4", 40 | "normalize-path": "^3.0.0", 41 | "nyc": "^15.1.0", 42 | "resolve": "^1.20.0" 43 | }, 44 | "nyc": { 45 | "reporter": [ 46 | "lcov", 47 | "text-summary" 48 | ] 49 | }, 50 | "prettier": { 51 | "singleQuote": true 52 | }, 53 | "keywords": [ 54 | "file", 55 | "find", 56 | "find-up", 57 | "findup", 58 | "glob", 59 | "match", 60 | "pattern", 61 | "resolve", 62 | "search" 63 | ] 64 | } 65 | -------------------------------------------------------------------------------- /test/support/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var normalizePath = require('normalize-path'); 5 | var resolve = require('resolve'); 6 | 7 | exports.normalize = function (filepath) { 8 | return filepath ? normalizePath(path.relative('.', filepath)) : null; 9 | }; 10 | 11 | exports.chdir = function (dir) { 12 | // store current cwd 13 | var orig = process.cwd(); 14 | // set cwd to the given `dir` 15 | process.chdir(dir); 16 | return function () { 17 | // restore original `cwd` 18 | process.chdir(orig); 19 | }; 20 | }; 21 | 22 | exports.npm = function npm(name) { 23 | return path.dirname(resolve.sync(name)); 24 | }; 25 | 26 | function matcherResult(pass, msg) { 27 | return { 28 | pass: pass, 29 | message: function () { 30 | return msg; 31 | }, 32 | }; 33 | } 34 | 35 | exports.expectExtras = { 36 | isPath: function (actual) { 37 | if (typeof actual === 'string') { 38 | return matcherResult(true, ''); 39 | } 40 | return matcherResult(false, '"' + actual + '" is not a string'); 41 | }, 42 | toHaveBasename: function (actual, basename) { 43 | var fileName = path.basename(actual); 44 | if (fileName === basename) { 45 | return matcherResult(true, ''); 46 | } 47 | return matcherResult( 48 | false, 49 | 'The basename of "' + actual + '" is not equal to "' + basename + '".' 50 | ); 51 | }, 52 | toHaveDirname: function (actual, dirname) { 53 | var filePath = path.dirname(path.resolve(actual)); 54 | var expected = path.resolve(dirname); 55 | if (filePath === expected) { 56 | return matcherResult(true, ''); 57 | } 58 | return matcherResult( 59 | false, 60 | 'The direname of "' + actual + '" is not equal to "' + expected + '".' 61 | ); 62 | }, 63 | }; 64 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 | # findup-sync 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 the first file matching a given pattern in the current directory or the nearest ancestor directory. 12 | 13 | Matching is done with [micromatch][micromatch], please report any matching related issues on that repository. 14 | 15 | ## Usage 16 | 17 | ```js 18 | var findup = require('findup-sync'); 19 | findup(patternOrPatterns [, micromatchOptions]); 20 | 21 | // Start looking in the CWD. 22 | var filepath1 = findup('{a,b}*.txt'); 23 | 24 | // Start looking somewhere else, and ignore case (probably a good idea). 25 | var filepath2 = findup('{a,b}*.txt', {cwd: '/some/path', nocase: true}); 26 | ``` 27 | 28 | ## API 29 | 30 | ### `findup(patterns, [options])` 31 | 32 | - `patterns` **{String|Array}**: Glob pattern(s) or file path(s) to match against. 33 | - `options` **{Object}**: Options to pass to [micromatch]. Note that if you want to start in a different directory than the current working directory, specify a `cwd` property here. 34 | - `returns` **{String}**: Returns the first matching file. 35 | 36 | ## License 37 | 38 | MIT 39 | 40 | 41 | 42 | [downloads-image]: https://img.shields.io/npm/dm/findup-sync.svg?style=flat-square 43 | [npm-url]: https://www.npmjs.com/package/findup-sync 44 | [npm-image]: https://img.shields.io/npm/v/findup-sync.svg?style=flat-square 45 | [ci-url]: https://github.com/gulpjs/findup-sync/actions?query=workflow:dev 46 | [ci-image]: https://img.shields.io/github/actions/workflow/status/gulpjs/findup-sync/dev.yml?branch=master&style=flat-square 47 | [coveralls-url]: https://coveralls.io/r/gulpjs/findup-sync 48 | [coveralls-image]: https://img.shields.io/coveralls/gulpjs/findup-sync/master.svg 49 | 50 | 51 | 52 | 53 | 54 | [micromatch]: http://github.com/micromatch/micromatch 55 | 56 | 57 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Module dependencies 5 | */ 6 | 7 | var fs = require('fs'); 8 | var path = require('path'); 9 | var isGlob = require('is-glob'); 10 | var resolveDir = require('resolve-dir'); 11 | var detect = require('detect-file'); 12 | var mm = require('micromatch'); 13 | 14 | /** 15 | * @param {String|Array} `pattern` Glob pattern or file path(s) to match against. 16 | * @param {Object} `options` Options to pass to [micromatch]. Note that if you want to start in a different directory than the current working directory, specify the `options.cwd` property here. 17 | * @return {String} Returns the first matching file. 18 | * @api public 19 | */ 20 | 21 | module.exports = function (patterns, options) { 22 | options = options || {}; 23 | var cwd = path.resolve(resolveDir(options.cwd || '')); 24 | 25 | if (typeof patterns === 'string') { 26 | return lookup(cwd, [patterns], options); 27 | } 28 | 29 | if (!Array.isArray(patterns)) { 30 | throw new TypeError( 31 | 'findup-sync expects a string or array as the first argument.' 32 | ); 33 | } 34 | 35 | return lookup(cwd, patterns, options); 36 | }; 37 | 38 | function lookup(cwd, patterns, options) { 39 | var len = patterns.length; 40 | var idx = -1; 41 | var res; 42 | 43 | while (++idx < len) { 44 | if (isGlob(patterns[idx])) { 45 | res = matchFile(cwd, patterns[idx], options); 46 | } else { 47 | res = findFile(cwd, patterns[idx], options); 48 | } 49 | if (res) { 50 | return res; 51 | } 52 | } 53 | 54 | var dir = path.dirname(cwd); 55 | if (dir === cwd) { 56 | return null; 57 | } 58 | return lookup(dir, patterns, options); 59 | } 60 | 61 | function matchFile(cwd, pattern, opts) { 62 | var isMatch = mm.matcher(pattern, opts); 63 | var files = tryReaddirSync(cwd); 64 | var len = files.length; 65 | var idx = -1; 66 | 67 | while (++idx < len) { 68 | var name = files[idx]; 69 | var fp = path.join(cwd, name); 70 | if (isMatch(name) || isMatch(fp)) { 71 | return fp; 72 | } 73 | } 74 | return null; 75 | } 76 | 77 | function findFile(cwd, filename, options) { 78 | var fp = path.resolve(cwd, filename); 79 | return detect(fp, options); 80 | } 81 | 82 | function tryReaddirSync(fp) { 83 | try { 84 | return fs.readdirSync(fp); 85 | } catch (err) { 86 | // Ignore error 87 | } 88 | /* istanbul ignore next */ 89 | return []; 90 | } 91 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | 6 | var expect = require('expect'); 7 | var home = require('homedir-polyfill'); 8 | var resolve = require('resolve'); 9 | 10 | var support = require('./support'); 11 | expect.extend(support.expectExtras); 12 | 13 | var findup = require('../'); 14 | 15 | var exists = fs.existsSync; 16 | var normalize = support.normalize; 17 | var chdir = support.chdir; 18 | var npm = support.npm; 19 | var isLinux = process.platform === 'linux'; 20 | var cwd, actual; 21 | 22 | describe('findup-sync', function () { 23 | before(function (done) { 24 | fs.writeFileSync(home() + '/_aaa.txt', ''); 25 | fs.writeFileSync(home() + '/_bbb.txt', ''); 26 | done(); 27 | }); 28 | 29 | after(function (done) { 30 | fs.unlinkSync(home() + '/_aaa.txt'); 31 | fs.unlinkSync(home() + '/_bbb.txt'); 32 | done(); 33 | }); 34 | 35 | it('should throw when the first arg is not a string or array:', function (cb) { 36 | try { 37 | findup(); 38 | cb(new Error('expected an error')); 39 | } catch (err) { 40 | expect(err.message).toEqual( 41 | 'findup-sync expects a string or array as the first argument.' 42 | ); 43 | cb(); 44 | } 45 | }); 46 | 47 | it('should work when no cwd is given', function (done) { 48 | var actual = findup('package.json'); 49 | expect(actual).toBeTruthy(); 50 | expect(actual).toHaveDirname(path.resolve(__dirname, '..')); 51 | expect(actual).toHaveBasename('package.json'); 52 | expect(normalize(findup('package.json'))).toEqual('package.json'); 53 | done(); 54 | }); 55 | 56 | it('should find files in a child directory', function (done) { 57 | var expected = path.resolve(__dirname, 'fixtures/a/b/file.txt'); 58 | var restore = chdir(path.resolve(__dirname, 'fixtures/a/b/c/d/e/f/g/h')); 59 | 60 | var actual = findup('a/b/file.txt'); 61 | expect(actual).toBeTruthy(); 62 | expect(exists(actual)).toBeTruthy(); 63 | expect(actual).toEqual(expected); 64 | restore(); 65 | done(); 66 | }); 67 | 68 | it('should find case sensitive files in a child directory', function (done) { 69 | var expected = path.resolve( 70 | __dirname, 71 | 'fixtures/a/b/', 72 | isLinux ? 'Mochafile.txt' : 'mochafile.txt' 73 | ); 74 | var restore = chdir(path.resolve(__dirname, 'fixtures/a/b/c/d/e/f/g/h')); 75 | 76 | var actual = findup('a/b/mochafile.txt', { nocase: true }); 77 | expect(actual).toBeTruthy(); 78 | expect(exists(actual)).toBeTruthy(); 79 | expect(actual).toEqual(expected); 80 | restore(); 81 | done(); 82 | }); 83 | 84 | it('should find files in a child directory relative to a cwd', function (done) { 85 | var expectedFile = path.resolve(__dirname, 'fixtures/a/b/file.txt'); 86 | var expectedA = path.resolve(__dirname, 'fixtures/a/a.txt'); 87 | var tempDir = chdir(path.resolve(__dirname, 'fixtures')); 88 | 89 | var actualFile = findup('a/b/file.txt', { cwd: 'a/b/c/d' }); 90 | expect(actualFile).toBeTruthy(); 91 | expect(exists(actualFile)).toBeTruthy(); 92 | expect(actualFile).toEqual(expectedFile); 93 | 94 | var actualA = findup('a.txt', { cwd: 'a/b/c/d/e/f' }); 95 | expect(actualA).toBeTruthy(); 96 | expect(exists(actualA)).toBeTruthy(); 97 | expect(actualA).toEqual(expectedA); 98 | tempDir(); 99 | done(); 100 | }); 101 | 102 | it('should find case sensitive files in a child directory relative to a cwd', function (done) { 103 | var expectedFile = path.resolve( 104 | __dirname, 105 | 'fixtures/a/b', 106 | isLinux ? 'Mochafile.txt' : 'mochafile.txt' 107 | ); 108 | var expectedA = path.resolve(__dirname, 'fixtures/a/a.txt'); 109 | var tempDir = chdir(path.resolve(__dirname, 'fixtures')); 110 | 111 | var actualFile = findup('a/b/mochafile.txt', { 112 | cwd: 'a/b/c/d', 113 | nocase: true, 114 | }); 115 | expect(actualFile).toBeTruthy(); 116 | expect(exists(actualFile)).toBeTruthy(); 117 | expect(actualFile).toEqual(expectedFile); 118 | 119 | var actualA = findup('a.txt', { cwd: 'a/b/c/d/e/f' }); 120 | expect(actualA).toBeTruthy(); 121 | expect(exists(actualA)).toBeTruthy(); 122 | expect(actualA).toEqual(expectedA); 123 | tempDir(); 124 | done(); 125 | }); 126 | 127 | it('should support normal (non-glob) file paths:', function (done) { 128 | var normPath = normalize( 129 | findup('package.json', { 130 | cwd: path.dirname(resolve.sync('normalize-path')), 131 | }) 132 | ); 133 | expect(normPath).toEqual('node_modules/normalize-path/package.json'); 134 | 135 | var isGlob = normalize( 136 | findup('package.json', { cwd: path.dirname(resolve.sync('is-glob')) }) 137 | ); 138 | expect(isGlob).toEqual('node_modules/is-glob/package.json'); 139 | 140 | cwd = path.dirname(resolve.sync('normalize-path')); 141 | var actual = findup('package.json', { cwd: cwd }); 142 | expect(actual).toHaveDirname(cwd); 143 | expect(actual).toHaveBasename('package.json'); 144 | 145 | actual = findup('c/package.json', { cwd: 'test/fixtures/a/b/c/d/e/f/g' }); 146 | expect(actual).toHaveBasename('package.json'); 147 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 148 | 149 | cwd = path.dirname(resolve.sync('is-glob')); 150 | actual = findup('package.json', { cwd: cwd }); 151 | expect(actual).toHaveDirname(cwd); 152 | expect(actual).toHaveBasename('package.json'); 153 | done(); 154 | }); 155 | 156 | it('should support normal (non-glob) case sensitive file paths:', function (done) { 157 | actual = findup('c/mochafile.txt', { 158 | cwd: 'test/fixtures/a/b/c/d/e/f/g', 159 | nocase: true, 160 | }); 161 | expect(actual).toHaveBasename(isLinux ? 'Mochafile.txt' : 'mochafile.txt'); 162 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 163 | done(); 164 | }); 165 | 166 | it('should support glob patterns', function (done) { 167 | expect( 168 | normalize( 169 | findup('**/c/package.json', { cwd: 'test/fixtures/a/b/c/d/e/f/g' }) 170 | ) 171 | ).toEqual('test/fixtures/a/b/c/package.json'); 172 | expect( 173 | normalize(findup('**/one.txt', { cwd: 'test/fixtures/a/b/c/d/e/f/g' })) 174 | ).toEqual('test/fixtures/a/b/c/d/one.txt'); 175 | expect( 176 | normalize(findup('**/two.txt', { cwd: 'test/fixtures/a/b/c/d/e/f/g' })) 177 | ).toEqual('test/fixtures/a/b/c/two.txt'); 178 | 179 | var pkg = normalize(findup('p*.json', { cwd: npm('micromatch') })); 180 | expect(pkg).toEqual('node_modules/micromatch/package.json'); 181 | 182 | var opts = { cwd: 'test/fixtures/a/b/c/d/e/f/g' }; 183 | 184 | actual = findup('**/c/package.json', opts); 185 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 186 | expect(actual).toHaveBasename('package.json'); 187 | 188 | actual = findup('c/package.json', opts); 189 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 190 | expect(actual).toHaveBasename('package.json'); 191 | 192 | actual = findup('**/ONE.txt', opts); 193 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 194 | expect(actual).toHaveBasename('ONE.txt'); 195 | 196 | actual = findup('**/two.txt', opts); 197 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 198 | expect(actual).toHaveBasename('two.txt'); 199 | 200 | cwd = npm('is-glob'); 201 | actual = findup('p*.json', { cwd: cwd }); 202 | expect(actual).toHaveDirname(cwd); 203 | expect(actual).toHaveBasename('package.json'); 204 | done(); 205 | }); 206 | 207 | it('should support case sensitive glob patterns', function (done) { 208 | expect( 209 | normalize( 210 | findup('**/c/mochafile.txt', { 211 | cwd: 'test/fixtures/a/b/c/d/e/f/g', 212 | nocase: true, 213 | }) 214 | ) 215 | ).toEqual('test/fixtures/a/b/c/Mochafile.txt'); 216 | expect( 217 | normalize( 218 | findup('**/one.txt', { 219 | cwd: 'test/fixtures/a/b/c/d/e/f/g', 220 | nocase: true, 221 | }) 222 | ) 223 | ).toEqual('test/fixtures/a/b/c/d/one.txt'); 224 | expect( 225 | normalize( 226 | findup('**/two.txt', { 227 | cwd: 'test/fixtures/a/b/c/d/e/f/g', 228 | nocase: true, 229 | }) 230 | ) 231 | ).toEqual('test/fixtures/a/b/c/two.txt'); 232 | 233 | expect( 234 | normalize(findup('mocha*', { cwd: 'test/fixtures/a/b/c', nocase: true })) 235 | ).toEqual('test/fixtures/a/b/c/Mochafile.txt'); 236 | 237 | var opts = { cwd: 'test/fixtures/a/b/c/d/e/f/g', nocase: true }; 238 | 239 | actual = findup('**/c/mochafile.txt', opts); 240 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 241 | expect(actual).toHaveBasename('Mochafile.txt'); 242 | 243 | actual = findup('c/mochafile.txt', opts); 244 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 245 | expect(actual).toHaveBasename(isLinux ? 'Mochafile.txt' : 'mochafile.txt'); 246 | 247 | opts.nocase = false; 248 | actual = findup('**/ONE.txt', opts); 249 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 250 | expect(actual).toHaveBasename('ONE.txt'); 251 | 252 | actual = findup('**/two.txt', opts); 253 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 254 | expect(actual).toHaveBasename('two.txt'); 255 | done(); 256 | }); 257 | 258 | it('should support arrays of glob patterns', function (done) { 259 | expect( 260 | normalize( 261 | findup(['**/c/package.json'], { cwd: 'test/fixtures/a/b/c/d/e/f/g' }) 262 | ) 263 | ).toEqual('test/fixtures/a/b/c/package.json'); 264 | expect( 265 | normalize(findup(['**/one.txt'], { cwd: 'test/fixtures/a/b/c/d/e/f/g' })) 266 | ).toEqual('test/fixtures/a/b/c/d/one.txt'); 267 | expect( 268 | normalize(findup(['**/two.txt'], { cwd: 'test/fixtures/a/b/c/d/e/f/g' })) 269 | ).toEqual('test/fixtures/a/b/c/two.txt'); 270 | 271 | var opts = { cwd: 'test/fixtures/a/b/c/d/e/f/g' }; 272 | 273 | actual = findup(['lslsl', '**/c/package.json'], opts); 274 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 275 | expect(actual).toHaveBasename('package.json'); 276 | 277 | actual = findup(['lslsl', 'c/package.json'], opts); 278 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 279 | expect(actual).toHaveBasename('package.json'); 280 | 281 | actual = findup(['lslsl', '**/ONE.txt'], opts); 282 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 283 | expect(actual).toHaveBasename('ONE.txt'); 284 | 285 | actual = findup(['lslsl', '**/two.txt'], opts); 286 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 287 | expect(actual).toHaveBasename('two.txt'); 288 | 289 | actual = findup(['lslsl', '**/blah.txt'], opts); 290 | expect(actual === null).toBeTruthy(); 291 | 292 | cwd = npm('is-glob'); 293 | actual = findup(['lslsl', 'p*.json'], { cwd: cwd }); 294 | expect(actual).toHaveDirname(cwd); 295 | expect(actual).toHaveBasename('package.json'); 296 | done(); 297 | }); 298 | 299 | it('should support micromatch `matchBase` option:', function (done) { 300 | var opts = { matchBase: true, cwd: 'test/fixtures/a/b/c/d/e/f/g' }; 301 | expect(normalize(findup('package.json', opts))).toEqual( 302 | 'test/fixtures/a/b/c/d/e/f/g/package.json' 303 | ); 304 | expect(normalize(findup('one.txt', opts))).toEqual( 305 | 'test/fixtures/a/b/c/d/one.txt' 306 | ); 307 | expect(normalize(findup('two.txt', opts))).toEqual( 308 | 'test/fixtures/a/b/c/two.txt' 309 | ); 310 | 311 | actual = findup('package.json', opts); 312 | expect(actual).toHaveBasename('package.json'); 313 | expect(actual).toHaveDirname('test/fixtures/a/b/c/d/e/f/g'); 314 | 315 | actual = findup('one.txt', opts); 316 | expect(actual).toHaveBasename('one.txt'); 317 | expect(actual).toHaveDirname('test/fixtures/a/b/c/d'); 318 | 319 | actual = findup('two.txt', opts); 320 | expect(actual).toHaveBasename('two.txt'); 321 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 322 | done(); 323 | }); 324 | 325 | it('should return `null` when no files are found:', function (done) { 326 | var dep = normalize( 327 | findup('*.foo', { cwd: path.dirname(resolve.sync('micromatch')) }) 328 | ); 329 | expect(dep).toEqual(null); 330 | expect(findup('**/b*.json', { cwd: npm('is-glob') })).toEqual(null); 331 | expect(findup('foo.json', { cwd: 'test/fixtures/a/b/c/d/e/f/g' })).toEqual( 332 | null 333 | ); 334 | expect( 335 | findup('foo.json', { 336 | cwd: 'test/fixtures/a/b/c/d/e/f/g', 337 | matchBase: true, 338 | }) 339 | ).toEqual(null); 340 | done(); 341 | }); 342 | 343 | it('should support finding file in immediate parent dir', function (done) { 344 | cwd = path.resolve(__dirname, 'fixtures/a/b/c'); 345 | var actual = findup('a.md', { cwd: cwd }); 346 | expect(actual).toHaveDirname(path.dirname(cwd)); 347 | expect(actual).toHaveBasename('a.md'); 348 | done(); 349 | }); 350 | 351 | it('should support micromatch `nocase` option:', function (done) { 352 | actual = findup('ONE.*', { cwd: 'test/fixtures/a/b/c/d' }); 353 | expect(actual).toHaveBasename('ONE.txt'); 354 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 355 | 356 | actual = findup('ONE.*', { cwd: 'test/fixtures/a/b/c/d', nocase: true }); 357 | expect(actual).toHaveBasename('one.txt'); 358 | expect(actual).toHaveDirname('test/fixtures/a/b/c/d'); 359 | done(); 360 | }); 361 | 362 | it('should find files from absolute paths:', function (done) { 363 | var actual = findup('package.json', { cwd: __dirname }); 364 | 365 | expect(actual).toHaveBasename('package.json'); 366 | expect(actual).toHaveDirname(path.resolve(__dirname, '..')); 367 | 368 | actual = findup('one.txt', { cwd: __dirname + '/fixtures/a' }); 369 | expect(actual).toHaveBasename('one.txt'); 370 | expect(actual).toHaveDirname('test/fixtures/a'); 371 | 372 | actual = findup('two.txt', { cwd: __dirname + '/fixtures/a/b/c' }); 373 | expect(actual).toHaveBasename('two.txt'); 374 | expect(actual).toHaveDirname('test/fixtures/a/b/c'); 375 | done(); 376 | }); 377 | 378 | it('should find files in user home:', function (done) { 379 | var actual = findup('*', { cwd: home() }); 380 | expect(actual).isPath(); 381 | expect(exists(actual)).toBeTruthy(); 382 | expect(actual).toHaveDirname(home()); 383 | done(); 384 | }); 385 | 386 | it('should find files in user home using tilde expansion:', function (done) { 387 | var actual = findup('*', { cwd: '~' }); 388 | expect(actual).isPath(); 389 | expect(exists(actual)).toBeTruthy(); 390 | expect(actual).toHaveDirname(home()); 391 | done(); 392 | }); 393 | 394 | it('should match files in cwd before searching up', function (done) { 395 | var actual = findup(['a.txt', 'a.md'], { 396 | cwd: __dirname + '/fixtures/a/b', 397 | }); 398 | expect(actual).toHaveBasename('a.md'); 399 | expect(actual).toHaveDirname('test/fixtures/a/b'); 400 | done(); 401 | }); 402 | }); 403 | --------------------------------------------------------------------------------