diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..bd96839 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# Enforce Unix newlines +*.* text eol=lf + +*.jpg binary +*.gif binary +*.png binary +*.jpeg binary \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2ccbe46..cb5fd92 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -/node_modules/ +*.DS_Store +*.sublime-* +node_modules +npm-debug.log +test/actual diff --git a/.jshintrc b/.jshintrc index 8718952..a82080d 100644 --- a/.jshintrc +++ b/.jshintrc @@ -1,15 +1,19 @@ { - "loopfunc": true, + "asi": false, + "boss": true, "curly": true, "eqeqeq": true, + "eqnull": true, + "esnext": true, "immed": true, - "latedef": true, + "latedef": false, + "laxbreak": true, + "laxcomma": false, "newcap": true, "noarg": true, + "node": true, "sub": true, "undef": true, "unused": true, - "boss": true, - "eqnull": true, - "node": true -} + "mocha": true +} \ No newline at end of file diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 84561e0..0000000 --- a/.npmignore +++ /dev/null @@ -1,4 +0,0 @@ -test -.travis.yml -.jshintrc -Gruntfile.js diff --git a/.travis.yml b/.travis.yml index 57a4c3f..09768f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,12 @@ +sudo: false language: node_js node_js: - - 0.8 - - '0.10' -before_install: - - npm update -g npm - - npm install -g grunt-cli + - "stable" + - "5" + - "4" + - "0.12" + - "0.10" +matrix: + fast_finish: true + allow_failures: + - node_js: "0.10" diff --git a/Gruntfile.js b/Gruntfile.js index 2f964a5..e42e164 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -4,22 +4,17 @@ module.exports = function(grunt) { // Project configuration. grunt.initConfig({ - nodeunit: { - files: ['test/**/*_test.js'], - }, jshint: { options: { jshintrc: '.jshintrc' }, - all: ['Gruntfile.js', 'lib/**/*.js', 'test/**/*.js'] + all: ['Gruntfile.js', '*.js'] } }); // Load plugins. grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.loadNpmTasks('grunt-contrib-nodeunit'); // Default task. - grunt.registerTask('default', ['jshint', 'nodeunit']); - + grunt.registerTask('default', ['jshint']); }; diff --git a/LICENSE-MIT b/LICENSE-MIT index bb2aad6..8bb6566 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,6 @@ -Copyright (c) 2013 "Cowboy" Ben Alman +The MIT License (MIT) + +Copyright (c) 2013-2016, "Cowboy" Ben Alman. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/README.md b/README.md index e396df5..22f8e0f 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,20 @@ -# findup-sync [![Build Status](https://secure.travis-ci.org/cowboy/node-findup-sync.png?branch=master)](http://travis-ci.org/cowboy/node-findup-sync) +# findup-sync [![Build Status](https://travis-ci.org/cowboy/node-findup-sync.svg)](https://travis-ci.org/cowboy/node-findup-sync) [![NPM version](https://badge.fury.io/js/findup-sync.svg)](http://badge.fury.io/js/findup-sync) -Find the first file matching a given pattern in the current directory or the nearest ancestor directory. +> Find the first file matching a given pattern in the current directory or the nearest ancestor directory. -## Getting Started -Install the module with: `npm install findup-sync` +Matching is done with [micromatch][], please report any matching related issues on that repository. + +## Install with [npm](npmjs.org) + +```bash +npm i findup-sync --save +``` + +## Usage ```js var findup = require('findup-sync'); +findup(patternOrPatterns [, micromatchOptions]); // Start looking in the CWD. var filepath1 = findup('{a,b}*.txt'); @@ -15,34 +23,45 @@ var filepath1 = findup('{a,b}*.txt'); var filepath2 = findup('{a,b}*.txt', {cwd: '/some/path', nocase: true}); ``` -## Usage - -```js -findup(patternOrPatterns [, minimatchOptions]) -``` +* `patterns` **{String|Array}**: Glob pattern(s) or file path(s) to match against. +* `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. +* `returns` **{String}**: Returns the first matching file. -### patternOrPatterns -Type: `String` or `Array` -Default: none +## Running tests -One or more wildcard glob patterns. Or just filenames. +Install dev dependencies: -### minimatchOptions -Type: `Object` -Default: `{}` +```bash +npm i -d && npm test +``` -Options to be passed to [minimatch](https://github.com/isaacs/minimatch). +## Contributing -Note that if you want to start in a different directory than the current working directory, specify a `cwd` property here. +In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/) -## Contributing -In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/). +For bugs and feature requests, [please create an issue](https://github.com/cowboy/node-findup-sync/issues). ## Release History -2015-09-14 0 v0.3.0 - updated glob to ~5.0. -2014-12-17 - v0.2.1 - updated to glob ~4.3. + +2015-01-30 - v0.4.0 - Refactored, not also uses [micromatch][] instead of minimatch. +2015-09-14 - v0.3.0 - updated glob to ~5.0. 2014-12-17 - v0.2.1 - updated to glob ~4.3. +2014-12-17 - v0.2.1 - Updated to glob 4.3. 2014-12-16 - v0.2.0 - Removed lodash, updated to glob 4.x. 2014-03-14 - v0.1.3 - Updated dependencies. 2013-03-08 - v0.1.2 - Updated dependencies. Fixed a Node 0.9.x bug. Updated unit tests to work cross-platform. 2012-11-15 - v0.1.1 - Now works without an options object. -2012-11-01 - v0.1.0 - Initial release. +2012-11-01 - v0.1.0 - Initial release. + +## Authors + +**"Cowboy" Ben Alman** + ++ [github/cowboy](https://github.com/cowboy) ++ [twitter/cowboy](http://twitter.com/cowboy) + +## License + +Copyright (c) 2012-2016 "Cowboy" Ben Alman +Released under the MIT license + +[micromatch]: http://github.com/jonschlinkert/micromatch diff --git a/index.js b/index.js new file mode 100644 index 0000000..cfa5690 --- /dev/null +++ b/index.js @@ -0,0 +1,87 @@ +'use strict'; + +/** + * Module dependencies + */ + +var fs = require('fs'); +var path = require('path'); +var isGlob = require('is-glob'); +var resolveDir = require('resolve-dir'); +var mm = require('micromatch'); + +/** + * @param {String|Array} `pattern` Glob pattern or file path(s) to match against. + * @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. + * @return {String} Returns the first matching file. + * @api public + */ + +module.exports = function(patterns, options) { + if (typeof patterns === 'string') { + return lookup(patterns, options); + } + + if (!Array.isArray(patterns)) { + throw new TypeError('findup-sync expects a string or array as the first argument.'); + } + + var len = patterns.length, i = -1; + while (++i < len) { + var res = lookup(patterns[i], options); + if (res) { + return res; + } + } + + return null; +}; + +function lookup(pattern, options) { + options = options || {}; + var cwd = resolveDir(options.cwd || ''); + if (isGlob(pattern)) { + return matchFile(cwd, pattern, options); + } else { + return findFile(cwd, pattern); + } +} + +function matchFile(cwd, pattern, opts) { + var isMatch = mm.matcher(pattern, opts); + var files = fs.readdirSync(cwd); + var len = files.length, i = -1; + + while (++i < len) { + var name = files[i]; + var fp = path.join(cwd, name); + if (isMatch(name) || isMatch(fp)) { + return fp; + } + } + + var dir = path.dirname(cwd); + if (dir === cwd) { + return null; + } + return matchFile(dir, pattern, opts); +} + +function findFile(cwd, filename) { + var fp = cwd ? (cwd + '/' + filename) : filename; + if (fs.existsSync(fp)) { + return fp; + } + + var segs = cwd.split(path.sep); + var len = segs.length; + + while (len--) { + cwd = segs.slice(0, len).join('/'); + fp = cwd + '/' + filename; + if (fs.existsSync(fp)) { + return fp; + } + } + return null; +} diff --git a/lib/findup-sync.js b/lib/findup-sync.js deleted file mode 100644 index 871a725..0000000 --- a/lib/findup-sync.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - * findup-sync - * https://github.com/cowboy/node-findup-sync - * - * Copyright (c) 2013 "Cowboy" Ben Alman - * Licensed under the MIT license. - */ - -'use strict'; - -// Nodejs libs. -var path = require('path'); - -// External libs. -var glob = require('glob'); - -// Search for a filename in the given directory or all parent directories. -module.exports = function(patterns, options) { - // Normalize patterns to an array. - if (!Array.isArray(patterns)) { patterns = [patterns]; } - // Create globOptions so that it can be modified without mutating the - // original object. - var globOptions = Object.create(options || {}); - globOptions.maxDepth = 1; - globOptions.cwd = path.resolve(globOptions.cwd || '.'); - - var files, lastpath; - do { - // Search for files matching patterns. - files = patterns.map(function(pattern) { - return glob.sync(pattern, globOptions); - }).reduce(function(a, b) { - return a.concat(b); - }).filter(function(entry, index, arr) { - return index === arr.indexOf(entry); - }); - // Return file if found. - if (files.length > 0) { - return path.resolve(path.join(globOptions.cwd, files[0])); - } - // Go up a directory. - lastpath = globOptions.cwd; - globOptions.cwd = path.resolve(globOptions.cwd, '..'); - // If parentpath is the same as basedir, we can't go any higher. - } while (globOptions.cwd !== lastpath); - - // No files were found! - return null; -}; diff --git a/package.json b/package.json index 534aa74..3200d63 100644 --- a/package.json +++ b/package.json @@ -3,41 +3,46 @@ "description": "Find the first file matching a given pattern in the current directory or the nearest ancestor directory.", "version": "0.3.0", "homepage": "https://github.com/cowboy/node-findup-sync", - "author": { - "name": "\"Cowboy\" Ben Alman", - "url": "http://benalman.com/" - }, - "repository": { - "type": "git", - "url": "git://github.com/cowboy/node-findup-sync.git" - }, + "author": "\"Cowboy\" Ben Alman (http://benalman.com)", + "repository": "cowboy/node-findup-sync", "bugs": { "url": "https://github.com/cowboy/node-findup-sync/issues" }, - "licenses": [ - { - "type": "MIT", - "url": "https://github.com/cowboy/node-findup-sync/blob/master/LICENSE-MIT" - } + "license": "MIT", + "files": [ + "index.js" ], - "main": "lib/findup-sync", + "main": "index.js", "engines": { - "node": ">= 0.6.0" + "node": ">= 0.8.0" }, "scripts": { - "test": "grunt nodeunit" + "test": "grunt && mocha" }, "dependencies": { - "glob": "~5.0.0" + "is-glob": "^2.0.1", + "micromatch": "^2.3.7", + "resolve-dir": "^0.1.0" }, "devDependencies": { - "grunt": "~0.4.5", - "grunt-contrib-jshint": "~0.11.3", - "grunt-contrib-nodeunit": "~0.3.3" + "grunt": "^0.4.5", + "grunt-contrib-jshint": "^0.12.0", + "is-absolute": "^0.2.3", + "minimist": "^1.2.0", + "mocha": "^2.4.5", + "normalize-path": "^2.0.1", + "resolve": "^1.1.7", + "user-home": "^2.0.0" }, "keywords": [ + "file", "find", + "find-up", + "findup", "glob", - "file" + "match", + "pattern", + "resolve", + "search" ] } diff --git a/test/findup-sync_test.js b/test/findup-sync_test.js deleted file mode 100644 index f8baf9e..0000000 --- a/test/findup-sync_test.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -// Nodejs lib. -var path = require('path'); - -var findup = require('../lib/findup-sync.js'); - -// Get a relative path. -var rel = function(abspath) { - return typeof abspath === 'string' ? path.relative('.', abspath) : abspath; -}; - -exports['findup'] = { - setUp: function(done) { - this.cwd = process.cwd(); - done(); - }, - tearDown: function(done) { - process.chdir(this.cwd); - done(); - }, - 'simple': function(test) { - test.expect(8); - var opts = {cwd: 'test/fixtures/a/b'}; - test.equal(rel(findup('foo.txt', opts)), path.normalize('test/fixtures/a/foo.txt'), 'should find files'); - test.equal(rel(findup('bar.txt', opts)), path.normalize('test/fixtures/a/b/bar.txt'), 'should find files'); - test.equal(rel(findup('a.txt', opts)), path.normalize('test/fixtures/a.txt'), 'should find files'); - test.equal(rel(findup('?.txt', opts)), path.normalize('test/fixtures/a.txt'), 'should support glob patterns'); - test.equal(rel(findup('*.txt', opts)), path.normalize('test/fixtures/a/b/bar.txt'), 'should find the first thing that matches the glob pattern'); - test.equal(rel(findup(['b*.txt', 'f*.txt'], opts)), path.normalize('test/fixtures/a/b/bar.txt'), 'should find the first thing that matches any of the glob patterns'); - test.equal(rel(findup(['f*.txt', 'b*.txt'], opts)), path.normalize('test/fixtures/a/b/bar.txt'), 'should find the first thing that matches any of the glob patterns'); - test.equal(findup('not-gonna-exist-i-hope.txt', opts), null, 'should returning null if no files found'); - test.done(); - }, - 'cwd': function(test) { - test.expect(8); - process.chdir('test/fixtures/a/b'); - test.equal(rel(findup('foo.txt')), path.normalize('../foo.txt'), 'should find files'); - test.equal(rel(findup('bar.txt')), 'bar.txt', 'should find files'); - test.equal(rel(findup('a.txt')), path.normalize('../../a.txt'), 'should find files'); - test.equal(rel(findup('?.txt')), path.normalize('../../a.txt'), 'should support glob patterns'); - test.equal(rel(findup('*.txt')), 'bar.txt', 'should find the first thing that matches the glob pattern'); - test.equal(rel(findup(['b*.txt', 'f*.txt'])), 'bar.txt', 'should find the first thing that matches any of the glob patterns'); - test.equal(rel(findup(['f*.txt', 'b*.txt'])), 'bar.txt', 'should find the first thing that matches any of the glob patterns'); - test.equal(findup('not-gonna-exist-i-hope.txt'), null, 'should returning null if no files found'); - test.done(); - }, -}; diff --git a/test/fixtures/a.txt b/test/fixtures/a/b/.config/waldo similarity index 100% rename from test/fixtures/a.txt rename to test/fixtures/a/b/.config/waldo diff --git a/test/fixtures/a/b/bar.txt b/test/fixtures/a/b/ONE.js similarity index 100% rename from test/fixtures/a/b/bar.txt rename to test/fixtures/a/b/ONE.js diff --git a/test/fixtures/a/foo.txt b/test/fixtures/a/b/a.md similarity index 100% rename from test/fixtures/a/foo.txt rename to test/fixtures/a/b/a.md diff --git a/test/fixtures/aaa.txt b/test/fixtures/a/b/c/ONE.txt similarity index 100% rename from test/fixtures/aaa.txt rename to test/fixtures/a/b/c/ONE.txt diff --git a/test/fixtures/a/b/c/d/e/f/g/h/i/j/package.json b/test/fixtures/a/b/c/d/e/f/g/h/i/j/package.json new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/a/b/c/d/e/f/g/h/i/package.json b/test/fixtures/a/b/c/d/e/f/g/h/i/package.json new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/a/b/c/d/e/f/g/h/package.json b/test/fixtures/a/b/c/d/e/f/g/h/package.json new file mode 100644 index 0000000..755d1eb --- /dev/null +++ b/test/fixtures/a/b/c/d/e/f/g/h/package.json @@ -0,0 +1,4 @@ +{ + "name": "h", + "description": "package.json from h." +} diff --git a/test/fixtures/a/b/c/d/e/f/g/package.json b/test/fixtures/a/b/c/d/e/f/g/package.json new file mode 100644 index 0000000..1dfe7a9 --- /dev/null +++ b/test/fixtures/a/b/c/d/e/f/g/package.json @@ -0,0 +1,4 @@ +{ + "name": "g", + "description": "package.json from g." +} diff --git a/test/fixtures/a/b/c/d/e/f/package.json b/test/fixtures/a/b/c/d/e/f/package.json new file mode 100644 index 0000000..9800c88 --- /dev/null +++ b/test/fixtures/a/b/c/d/e/f/package.json @@ -0,0 +1,4 @@ +{ + "name": "f", + "description": "package.json from f." +} diff --git a/test/fixtures/a/b/c/d/e/package.json b/test/fixtures/a/b/c/d/e/package.json new file mode 100644 index 0000000..9648987 --- /dev/null +++ b/test/fixtures/a/b/c/d/e/package.json @@ -0,0 +1,4 @@ +{ + "name": "e", + "description": "package.json from e." +} diff --git a/test/fixtures/a/b/c/d/one.txt b/test/fixtures/a/b/c/d/one.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/a/b/c/d/package.json b/test/fixtures/a/b/c/d/package.json new file mode 100644 index 0000000..3134281 --- /dev/null +++ b/test/fixtures/a/b/c/d/package.json @@ -0,0 +1,4 @@ +{ + "name": "d", + "description": "package.json from d." +} diff --git a/test/fixtures/a/b/c/package.json b/test/fixtures/a/b/c/package.json new file mode 100644 index 0000000..fc3b934 --- /dev/null +++ b/test/fixtures/a/b/c/package.json @@ -0,0 +1,4 @@ +{ + "name": "c", + "description": "package.json from c." +} diff --git a/test/fixtures/a/b/c/two.txt b/test/fixtures/a/b/c/two.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/a/b/one.txt b/test/fixtures/a/b/one.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/a/b/package.json b/test/fixtures/a/b/package.json new file mode 100644 index 0000000..82b7ba8 --- /dev/null +++ b/test/fixtures/a/b/package.json @@ -0,0 +1,4 @@ +{ + "name": "b", + "description": "package.json from b." +} diff --git a/test/fixtures/a/one.txt b/test/fixtures/a/one.txt new file mode 100644 index 0000000..e69de29 diff --git a/test/fixtures/a/package.json b/test/fixtures/a/package.json new file mode 100644 index 0000000..d67dd39 --- /dev/null +++ b/test/fixtures/a/package.json @@ -0,0 +1,4 @@ +{ + "name": "a", + "description": "package.json from a." +} diff --git a/test/fixtures/package.json b/test/fixtures/package.json new file mode 100644 index 0000000..3703b8e --- /dev/null +++ b/test/fixtures/package.json @@ -0,0 +1,4 @@ +{ + "name": "fixtures", + "description": "package.json from ./fixtures." +} diff --git a/test/test.js b/test/test.js new file mode 100644 index 0000000..1fbce7c --- /dev/null +++ b/test/test.js @@ -0,0 +1,246 @@ +'use strict'; + +require('mocha'); +var argv = require('minimist')(process.argv.slice(2)); +var fs = require('fs'); +var path = require('path'); +var assert = require('assert'); +var expand = require('resolve-dir'); +var norm = require('normalize-path'); +var home = require('user-home'); +var isAbsolute = require('is-absolute'); +var resolve = require('resolve'); +var cwd, actual, opts; + +var findup = require('../'); + +function normalize(fp) { + return fp ? norm(path.relative('.', fp)) : null; +} + +if (argv.bench) { + var b = path.join(__dirname, 'benchmark/code', argv.bench); + console.log(b); + findup = require(b); +} + +assert.isPath = function (fp, basename) { + assert(fp); + assert.equal(typeof fp, 'string'); +}; + +assert.isAbsolute = function (fp) { + assert(fp); + assert(isAbsolute(fp)); +}; + +assert.exists = function (fp) { + assert(fp); + try { + fs.statSync(fp); + } catch(err) { + assert(fp, err); + } +}; + +assert.basename = function (fp, basename) { + assert(fp); + assert.equal(path.basename(fp), basename); +}; + +assert.dirname = function (fp, dirname) { + assert(fp); + assert.equal(path.dirname(path.resolve(fp)), path.resolve(dirname)); +}; + +function npm(name) { + return path.dirname(resolve.sync(name)); +} + +describe('findup-sync', function () { + before(function () { + fs.writeFileSync(home + '/_aaa.txt', ''); + fs.writeFileSync(home + '/_bbb.txt', ''); + }); + after(function () { + fs.unlinkSync(home + '/_aaa.txt'); + fs.unlinkSync(home + '/_bbb.txt'); + }); + + it('should throw when the first arg is not a string or array:', function(cb) { + try { + findup(); + cb(new Error('expected an error')); + } catch (err) { + assert.equal(err.message, 'findup-sync expects a string or array as the first argument.'); + cb(); + } + }); + + it('should work when no cwd is given', function () { + var actual = findup('package.json'); + assert(actual); + assert.dirname(actual, path.resolve(__dirname, '..')); + assert.basename(actual, 'package.json'); + assert.equal(normalize(findup('package.json')), 'package.json'); + }); + + it('should support normal (non-glob) file paths:', function () { + var normPath = normalize(findup('package.json', {cwd: path.dirname(resolve.sync('normalize-path'))})); + assert.equal(normPath, 'node_modules/normalize-path/package.json'); + + var isGlob = normalize(findup('package.json', {cwd: path.dirname(resolve.sync('is-glob'))})); + assert.equal(isGlob, 'node_modules/is-glob/package.json'); + + cwd = path.dirname(resolve.sync('normalize-path')); + var actual = findup('package.json', {cwd: cwd}); + assert.dirname(actual, cwd); + assert.basename(actual, 'package.json'); + + actual = findup('c/package.json', {cwd: 'test/fixtures/a/b/c/d/e/f/g'}); + assert.basename(actual, 'package.json'); + assert.dirname(actual, 'test/fixtures/a/b/c'); + + cwd = path.dirname(resolve.sync('is-glob')); + actual = findup('package.json', {cwd: cwd}); + assert.dirname(actual, cwd); + assert.basename(actual, 'package.json'); + }); + + it('should support glob patterns', function() { + assert.equal(normalize(findup('**/c/package.json', {cwd: 'test/fixtures/a/b/c/d/e/f/g'})), 'test/fixtures/a/b/c/package.json'); + assert.equal(normalize(findup('**/one.txt', {cwd: 'test/fixtures/a/b/c/d/e/f/g'})), 'test/fixtures/a/b/c/d/one.txt'); + assert.equal(normalize(findup('**/two.txt', {cwd: 'test/fixtures/a/b/c/d/e/f/g'})), 'test/fixtures/a/b/c/two.txt'); + + var pkg = normalize(findup('p*.json', {cwd: npm('micromatch')})); + assert.equal(pkg, 'node_modules/micromatch/package.json'); + + var opts = {cwd: 'test/fixtures/a/b/c/d/e/f/g'}; + + actual = findup('**/c/package.json', opts); + assert.dirname(actual, 'test/fixtures/a/b/c'); + assert.basename(actual, 'package.json'); + + actual = findup('c/package.json', opts); + assert.dirname(actual, 'test/fixtures/a/b/c'); + assert.basename(actual, 'package.json'); + + actual = findup('**/ONE.txt', opts); + assert.dirname(actual, 'test/fixtures/a/b/c'); + assert.basename(actual, 'ONE.txt'); + + actual = findup('**/two.txt', opts); + assert.dirname(actual, 'test/fixtures/a/b/c'); + assert.basename(actual, 'two.txt'); + + cwd = npm('is-glob'); + actual = findup('p*.json', {cwd: cwd}); + assert.dirname(actual, cwd); + assert.basename(actual, 'package.json'); + }); + + it('should support arrays of glob patterns', function() { + assert.equal(normalize(findup(['**/c/package.json'], {cwd: 'test/fixtures/a/b/c/d/e/f/g'})), 'test/fixtures/a/b/c/package.json'); + assert.equal(normalize(findup(['**/one.txt'], {cwd: 'test/fixtures/a/b/c/d/e/f/g'})), 'test/fixtures/a/b/c/d/one.txt'); + assert.equal(normalize(findup(['**/two.txt'], {cwd: 'test/fixtures/a/b/c/d/e/f/g'})), 'test/fixtures/a/b/c/two.txt'); + + var opts = {cwd: 'test/fixtures/a/b/c/d/e/f/g'}; + + actual = findup(['lslsl', '**/c/package.json'], opts); + assert.dirname(actual, 'test/fixtures/a/b/c'); + assert.basename(actual, 'package.json'); + + actual = findup(['lslsl', 'c/package.json'], opts); + assert.dirname(actual, 'test/fixtures/a/b/c'); + assert.basename(actual, 'package.json'); + + actual = findup(['lslsl', '**/ONE.txt'], opts); + assert.dirname(actual, 'test/fixtures/a/b/c'); + assert.basename(actual, 'ONE.txt'); + + actual = findup(['lslsl', '**/two.txt'], opts); + assert.dirname(actual, 'test/fixtures/a/b/c'); + assert.basename(actual, 'two.txt'); + + actual = findup(['lslsl', '**/blah.txt'], opts); + assert(actual === null); + + cwd = npm('is-glob'); + actual = findup(['lslsl', 'p*.json'], {cwd: cwd}); + assert.dirname(actual, cwd); + assert.basename(actual, 'package.json'); + }); + + it('should support micromatch `matchBase` option:', function() { + var opts = { matchBase: true, cwd: 'test/fixtures/a/b/c/d/e/f/g' }; + assert.equal(normalize(findup('package.json', opts)), 'test/fixtures/a/b/c/d/e/f/g/package.json'); + assert.equal(normalize(findup('one.txt', opts)), 'test/fixtures/a/b/c/d/one.txt'); + assert.equal(normalize(findup('two.txt', opts)), 'test/fixtures/a/b/c/two.txt'); + + actual = findup('package.json', opts); + assert.basename(actual, 'package.json'); + assert.dirname(actual, 'test/fixtures/a/b/c/d/e/f/g'); + + actual = findup('one.txt', opts); + assert.basename(actual, 'one.txt'); + assert.dirname(actual, 'test/fixtures/a/b/c/d'); + + actual = findup('two.txt', opts); + assert.basename(actual, 'two.txt'); + assert.dirname(actual, 'test/fixtures/a/b/c'); + }); + + it('should return `null` when no files are found:', function() { + var dep = normalize(findup('*.foo', {cwd: path.dirname(resolve.sync('micromatch'))})); + assert.equal(dep, null); + assert.equal(findup('**/b*.json', {cwd: npm('is-glob')}), null); + assert.equal(findup('foo.json', {cwd: 'test/fixtures/a/b/c/d/e/f/g'}), null); + assert.equal(findup('foo.json', {cwd: 'test/fixtures/a/b/c/d/e/f/g', matchBase: true}), null); + }); + + it('should support finding file in immediate parent dir', function () { + cwd = path.resolve(__dirname, 'fixtures/a/b/c'); + var actual = findup('a.md', { cwd: cwd }); + assert.dirname(actual, path.dirname(cwd)); + assert.basename(actual, 'a.md'); + }); + + it('should support micromatch `nocase` option:', function () { + actual = findup('ONE.*', { cwd: 'test/fixtures/a/b/c/d' }); + assert.basename(actual, 'ONE.txt'); + assert.dirname(actual, 'test/fixtures/a/b/c'); + + actual = findup('ONE.*', { cwd: 'test/fixtures/a/b/c/d', nocase: true }); + assert.basename(actual, 'one.txt'); + assert.dirname(actual, 'test/fixtures/a/b/c/d'); + }); + + it('should find files from absolute paths:', function () { + var actual = findup('package.json', { cwd: __dirname }) + + assert.basename(actual, 'package.json'); + assert.dirname(actual, path.resolve(__dirname, '..')); + + actual = findup('one.txt', { cwd: __dirname + '/fixtures/a' }); + assert.basename(actual, 'one.txt'); + assert.dirname(actual, 'test/fixtures/a'); + + actual = findup('two.txt', { cwd: __dirname + '/fixtures/a/b/c' }); + assert.basename(actual, 'two.txt'); + assert.dirname(actual, 'test/fixtures/a/b/c'); + }); + + it('should find files in user home:', function () { + var actual = findup('*', { cwd: home }); + assert.isPath(actual); + assert.exists(actual); + assert.dirname(actual, home); + }); + + it('should find files in user home using tilde expansion:', function () { + var actual = findup('*', { cwd: '~' }); + assert.isPath(actual); + assert.exists(actual); + assert.dirname(actual, home); + }); +});