-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 731cedd
Showing
8 changed files
with
319 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*.sublime-* | ||
node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
# then-recursively-search | ||
|
||
A Promise-based Node module that recursively searches for a file, moving up the directory tree until found. | ||
|
||
Adheres to the `standard` coding style (click below for more information): | ||
|
||
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard#javascript-standard-style) | ||
|
||
|
||
## Why | ||
|
||
I needed to start my GitHub/NPM publishing journey somewhere, and this seemed like a good starting point. I wanted it to be [FIRST](https://addyosmani.com/first/). | ||
|
||
|
||
## How to use it | ||
|
||
### Install | ||
|
||
``` | ||
npm install then-recursively-search --save | ||
``` | ||
|
||
|
||
### API | ||
|
||
#### `fn(startIn, filename)` | ||
|
||
* @param {String} `startIn` - The directory to begin searching in. | ||
* @param {String} `filename` - The name (including extension) of the file to search for. | ||
* @return {Promise} - Fulfilled with the complete path to the file (if found); otherwise, rejected. | ||
|
||
|
||
#### Example | ||
|
||
``` | ||
const findFile = require('then-recursively-search') | ||
findFile(__dirname, 'package.json') | ||
.then(function (pathToFile) { | ||
... | ||
}) | ||
``` | ||
|
||
|
||
## Test | ||
|
||
Couldn't be easier... | ||
|
||
``` | ||
npm test | ||
``` | ||
|
||
|
||
## License | ||
|
||
**ISC** | ||
|
||
Copyright (c) 2016, [David Passarelli](mailto:dpassarelli@camelotcg.com) | ||
|
||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
'use strict' | ||
|
||
const Promise = require('bluebird') | ||
const debug = require('debug')('then-recursively-search') | ||
const fs = Promise.promisifyAll(require('fs')) | ||
const path = require('path') | ||
|
||
module.exports = _findFile | ||
|
||
/** | ||
* Recursively search a directory tree for the specified file. | ||
* | ||
* @param {String} startIn The directory to begin searching in. | ||
* | ||
* @param {String} filename The name (including extension) of the file to search for. | ||
* | ||
* @return {Promise} Fulfilled with the complete path to the file (if found); otherwise, rejected. | ||
*/ | ||
function _findFile (startIn, filename) { | ||
debug('searching for "%s" in "%s"', filename, startIn) | ||
|
||
return Promise.try(function () { | ||
if (startIn == null) { | ||
throw new Error('The required parameter "startIn" is missing.') | ||
} | ||
|
||
if (filename == null) { | ||
throw new Error('The required parameter "filename" is missing.') | ||
} | ||
|
||
return fs.readdirAsync(startIn) | ||
.then(function (contents) { | ||
const parentDir = path.dirname(startIn) | ||
|
||
if (~contents.indexOf(filename)) { | ||
debug('found!') | ||
return path.join(startIn, filename) | ||
} | ||
|
||
if (parentDir === startIn) { | ||
debug('not found') | ||
throw new Error('File not found.') | ||
} | ||
|
||
return _findFile(parentDir, filename) | ||
}) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
{ | ||
"name": "then-recursively-search", | ||
"version": "1.0.0", | ||
"description": "A Promise-based utility that recursively searches for a file, moving up the directory tree until found.", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "mocha", | ||
"lint": "standard" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/dpassarelli/node-then-recursively-search.git" | ||
}, | ||
"keywords": [ | ||
"file", | ||
"search", | ||
"recursive", | ||
"promise" | ||
], | ||
"author": "David Passarelli <dpassarelli@camelotcg.com>", | ||
"license": "ISC", | ||
"bugs": { | ||
"url": "https://github.com/dpassarelli/node-then-recursively-search/issues" | ||
}, | ||
"homepage": "https://github.com/dpassarelli/node-then-recursively-search#readme", | ||
"devDependencies": { | ||
"chai": "^3.5.0", | ||
"chai-as-promised": "^5.3.0", | ||
"mocha": "^3.0.2", | ||
"mock-fs": "^3.11.0", | ||
"standard": "^7.1.2" | ||
}, | ||
"dependencies": { | ||
"bluebird": "^3.4.1", | ||
"debug": "^2.2.0" | ||
}, | ||
"engines": { | ||
"node": ">=0.12.x" | ||
}, | ||
"standard": { | ||
"ignore": [ | ||
"build/" | ||
], | ||
"global": [ | ||
"expect" | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
const chai = require('chai') | ||
global.expect = chai.expect | ||
|
||
chai.use(require('chai-as-promised')) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--reporter spec | ||
--ui bdd | ||
--require test/common.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* eslint-env mocha */ | ||
const mockFS = require('mock-fs') | ||
const path = require('path') | ||
|
||
/** | ||
* Code under test. | ||
* @type {Function} | ||
*/ | ||
const T = require('../index.js') | ||
|
||
describe('fulfillment', function () { | ||
before(function () { | ||
mockFS({ | ||
one: { | ||
'one.txt': 'test file', | ||
'two.txt': 'test file', | ||
two: { | ||
'two.txt': 'test file', | ||
three: { | ||
'three.txt': 'test file', | ||
four: {} | ||
} | ||
} | ||
} | ||
}) | ||
}) | ||
|
||
after(function () { | ||
mockFS.restore() | ||
}) | ||
|
||
it('should be fulfilled if "filename" exists in "startIn"', function () { | ||
const startIn = path.join(process.cwd(), './one/two/three') | ||
const filename = 'three.txt' | ||
|
||
/** | ||
* The expected fulfillment value. | ||
* @type {String} | ||
*/ | ||
const expected = path.join(process.cwd(), './one/two/three/three.txt') | ||
|
||
return expect(T(startIn, filename)).to.be.eventually.fulfilled.with.string(expected) | ||
}) | ||
|
||
it('should be fulfilled if "filename" exists in directory more than one level above "startIn"', function () { | ||
const startIn = path.join(process.cwd(), './one/two/three') | ||
const filename = 'one.txt' | ||
|
||
/** | ||
* The expected fulfillment value. | ||
* @type {String} | ||
*/ | ||
const expected = path.join(process.cwd(), './one/one.txt') | ||
|
||
return expect(T(startIn, filename)).to.be.eventually.fulfilled.with.string(expected) | ||
}) | ||
|
||
it('should be fulfilled with the first match, if "filename" exists in more than one parent directory', function () { | ||
const startIn = path.join(process.cwd(), './one/two/three') | ||
const filename = 'two.txt' | ||
|
||
/** | ||
* The expected fulfillment value. | ||
* @type {String} | ||
*/ | ||
const expected = path.join(process.cwd(), './one/two/two.txt') | ||
|
||
return expect(T(startIn, filename)).to.be.eventually.fulfilled.with.string(expected) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
/* eslint-env mocha */ | ||
const mockFS = require('mock-fs') | ||
const path = require('path') | ||
|
||
/** | ||
* Code under test. | ||
* @type {Function} | ||
*/ | ||
const T = require('../index.js') | ||
|
||
/** | ||
* The error message thrown when the first parameter is missing. | ||
* @type {String} | ||
*/ | ||
const ERR_MISSING_DIR = 'The required parameter "startIn" is missing.' | ||
|
||
/** | ||
* The error message thrown when the second parameter is missing. | ||
* @type {String} | ||
*/ | ||
const ERR_MISSING_FILE = 'The required parameter "filename" is missing.' | ||
|
||
/** | ||
* The error message thrown when the file cannot be found (in any directory). | ||
* @type {String} | ||
*/ | ||
const ERR_404 = 'File not found.' | ||
|
||
describe('rejections', function () { | ||
before(function () { | ||
mockFS({ | ||
one: { | ||
'one.txt': 'test file', | ||
'two.txt': 'test file', | ||
two: { | ||
'two.txt': 'test file', | ||
three: { | ||
'three.txt': 'test file', | ||
four: {} | ||
} | ||
} | ||
} | ||
}) | ||
}) | ||
|
||
after(function () { | ||
mockFS.restore() | ||
}) | ||
|
||
it('should be rejected if no parameter values are provided', function () { | ||
return expect(T()).to.be.eventually.rejected.with.property('message', ERR_MISSING_DIR) | ||
}) | ||
|
||
it('should be rejected if the "startIn" parameter is provided, but not "filename"', function () { | ||
return expect(T('C:/')).to.be.eventually.rejected.with.property('message', ERR_MISSING_FILE) | ||
}) | ||
|
||
it('should be rejected if the value for "startIn" is not a string', function () { | ||
return expect(T(123, 'test.txt')).to.be.eventually.rejected.with.instanceof(TypeError).and.property('message').match(/path must be a string/i) | ||
}) | ||
|
||
it('should be rejected if the file does not exist anywhere in the specified directory (or any parent directories)', function () { | ||
const startIn = path.join(process.cwd(), './one/two/three/four') | ||
const filename = 'dne.txt' | ||
|
||
return expect(T(startIn, filename)).to.be.eventually.rejected.with.property('message', ERR_404) | ||
}) | ||
|
||
it('should be rejected if the directory specified by "startIn" does not exist', function () { | ||
const startIn = path.join(process.cwd(), './one/two/three/four/five') | ||
const filename = 'dne.txt' | ||
|
||
return expect(T(startIn, filename)).to.be.eventually.rejected.with.property('code', 'ENOENT') | ||
}) | ||
|
||
it('should be rejected if "filename" exists, but with a different case', function () { | ||
const startIn = path.join(process.cwd(), './one/two/three') | ||
const filename = 'ONE.txt' | ||
|
||
return expect(T(startIn, filename)).to.be.eventually.rejected.with.property('message', ERR_404) | ||
}) | ||
}) |