├── test ├── fixtures │ ├── file.js │ ├── readme.md │ ├── .hiddenfile │ ├── a │ │ ├── b │ │ │ ├── file_b1.js │ │ │ └── c │ │ │ │ └── file_c1.js │ │ ├── file_a1.js │ │ └── file_a2.js │ └── .hiddendir │ │ └── file.js ├── helpers │ └── index.js └── walk.js ├── .travis.yml ├── .gitignore ├── powerwalker.png ├── .github ├── funding.yml └── workflows │ └── ci.yml ├── appveyor.yml ├── example.js ├── package.json ├── license ├── index.js └── readme.md /test/fixtures/file.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/readme.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/.hiddenfile: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/a/b/file_b1.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/a/file_a1.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/a/file_a2.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/.hiddendir/file.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/a/b/c/file_c1.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 8 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | package-lock.json 4 | -------------------------------------------------------------------------------- /powerwalker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terkelg/powerwalker/HEAD/powerwalker.png -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: terkelg 4 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | nodejs_version: "8" 3 | 4 | install: 5 | - ps: Install-Product node $env:nodejs_version 6 | - npm install --global npm@latest 7 | - npm install 8 | 9 | test_script: 10 | - node --version 11 | - npm --version 12 | - npm test 13 | 14 | build: off 15 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | const { walk, walkSync } = require('./src'); 2 | const path = require('path'); 3 | 4 | (async function() { 5 | let ls = walkSync('../'); 6 | console.log(ls.length) 7 | 8 | ls = await walk('../', { cwd: 'test/fixtures/a/b', relative: true }); 9 | console.log(ls) 10 | 11 | })().catch(console.log); 12 | -------------------------------------------------------------------------------- /test/helpers/index.js: -------------------------------------------------------------------------------- 1 | const isWin = process.platform === 'win32'; 2 | 3 | // unixify path for cross-platform testing 4 | function unixify(arr) { 5 | return isWin ? arr.map(str => { 6 | return Array.isArray(str) ? unixify(str) : str.replace(/\\/g, '/'); 7 | }) : arr; 8 | } 9 | 10 | function toIgnore(str) { 11 | return !str.includes('.DS_Store'); 12 | } 13 | 14 | function order(arr) { 15 | return arr.filter(str => { 16 | return Array.isArray(str) ? order(str) : toIgnore(str); 17 | }).sort(); 18 | } 19 | 20 | module.exports = { unixify, order }; 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "powerwalker", 3 | "version": "0.1.2", 4 | "description": "walk directories recursively", 5 | "homepage": "https://github.com/terkelg/powerwalker", 6 | "main": "index.js", 7 | "author": { 8 | "name": "Terkel Gjervig", 9 | "email": "terkel@terkel.com", 10 | "url": "https://terkel.com" 11 | }, 12 | "scripts": { 13 | "test": "tape test/*.js | tap-spec" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "https://github.com/terkelg/powerwalker" 18 | }, 19 | "keywords": [ 20 | "walk", 21 | "walker", 22 | "directories", 23 | "files", 24 | "list", 25 | "dir" 26 | ], 27 | "license": "MIT", 28 | "dependencies": { 29 | "1d": "^0.1.1" 30 | }, 31 | "devDependencies": { 32 | "tap-spec": "^5.0.0", 33 | "tape": "^4.13.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: push 4 | 5 | jobs: 6 | test: 7 | name: Node.js v${{ matrix.nodejs }} (${{ matrix.os }}) 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | nodejs: [10, 12] 12 | os: [ubuntu-latest] 13 | steps: 14 | - uses: actions/checkout@v1 15 | with: 16 | fetch-depth: 1 17 | 18 | - uses: actions/setup-node@v1 19 | with: 20 | node-version: ${{ matrix.nodejs }} 21 | 22 | - name: Install 23 | run: | 24 | npm install 25 | npm install -g nyc 26 | 27 | - name: Test w/ Coverage 28 | run: npx nyc npm test 29 | 30 | - name: Report 31 | if: matrix.nodejs >= 12 32 | run: | 33 | npx nyc report --reporter=text-lcov > coverage.lcov 34 | bash <(curl -s https://codecov.io/bash) 35 | env: 36 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 37 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Terkel Gjervig Nielsen 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 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const flat = require('1d'); 3 | const path = require('path'); 4 | const { promisify } = require('util'); 5 | 6 | const stat = promisify(fs.lstat); 7 | const readdir = promisify(fs.readdir); 8 | 9 | /** 10 | * List all files in a directory recursively, async 11 | * @param {String} dir Start directory 12 | * @param {Object} [options] Options object 13 | * @param {String} [options.maxdepth=Infinity] Max walker depth 14 | * @param {String} [options.flatten=true] Flatten the output array 15 | * @param {String} [options.relative=true] Use relative or absolute paths 16 | * @param {String} [options.cwd='.'] Define custom current working directory 17 | * @returns {Array} List of files and directories 18 | */ 19 | async function walk(dir, {maxdepth = Infinity, flatten = true, filesonly = false, relative = true, cwd = '.'} = {}) { 20 | const format = file => relative ? path.relative(cwd, file) : file; 21 | async function walker(dir, depth = 0) { 22 | if (dir === '') dir = cwd; 23 | if (depth >= maxdepth) return format(dir); 24 | if ((await stat(dir)).isDirectory()) { 25 | depth++; 26 | const files = await readdir(dir); 27 | const arr = await Promise.all(files.map(async file => walker(path.join(dir, file), depth))); 28 | if (filesonly) return arr 29 | if (cwd === dir && relative) { 30 | const {dir:prev, base} = path.parse(dir); 31 | return arr.concat(path.relative(prev, base) || path.relative(cwd, base)); 32 | } 33 | return arr.concat(format(dir)); 34 | } 35 | return format(dir); 36 | } 37 | const joined = path.isAbsolute(dir) ? dir : path.join(cwd=path.resolve(cwd), dir); 38 | const start = relative ? joined : path.resolve(joined); 39 | return flatten ? flat(await walker(start)) : walker(start); 40 | } 41 | 42 | module.exports = walk; 43 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

2 | Power Walker 3 |

4 | 5 |

Power Walker

6 | 7 |

8 | 9 | version 10 | 11 | 12 | travis 13 | 14 | 15 | AppVeyor 16 | 17 | 18 | downloads 19 | 20 |

21 | 22 |

Walk directories recursively

23 | 24 |
25 | 26 | 27 | > "All truly great thoughts are conceived by walking" 28 | > – Friedrich Nietzsche 29 | 30 | 31 | ## Installation 32 | 33 | ``` 34 | npm install powerwalker 35 | ``` 36 | 37 | 38 | ## Usage 39 | 40 | ```js 41 | const walk = require('powerwalker'); 42 | 43 | let files = await walk('path/to/walk'); 44 | ``` 45 | 46 | ## API 47 | 48 | 49 | ### walk(dir, options) 50 | 51 | Type: `Promise`
52 | Returns: `Array` 53 | 54 | List all files and directories in `dir` recursively. 55 | 56 | #### dir 57 | 58 | Type: `String` 59 | 60 | A directory path to walk recursively. 61 | 62 | #### options 63 | 64 | Type: `Object`
65 | Default: `{ maxdepth: Infinity, flatten: true, filesonly: false }` 66 | 67 | Optional options object. 68 | 69 | #### options.maxdepth 70 | 71 | Type: `Number`
72 | Default: `Infinity` 73 | 74 | Max number of directories to walk before stopping. 75 | 76 | #### options.flatten 77 | 78 | Type: `Boolean`
79 | Default: `true` 80 | 81 | Option to flatten the output to a 1D array. 82 | 83 | #### options.filesonly 84 | 85 | Type: `Boolean`
86 | Default: `false` 87 | 88 | Exclude directories from result. 89 | 90 | #### options.relative 91 | 92 | Type: `Boolean`
93 | Default: `true` 94 | 95 | Return relative paths, or absolute paths. 96 | 97 | #### options.cwd 98 | 99 | Type: `String`
100 | Default: `'.'` 101 | 102 | Custom working directory. All paths are relative to this. 103 | 104 | 105 | ## License 106 | 107 | MIT © [Terkel Gjervig](https://terkel.com) 108 | -------------------------------------------------------------------------------- /test/walk.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const walk = require('../'); 3 | const test = require('tape'); 4 | const { unixify, order } = require('./helpers'); 5 | 6 | const isMatch = async (t, dir, expect, opts) => { 7 | let files = order(unixify(await walk(dir, opts))); 8 | if (Array.isArray(expect)) { 9 | t.equal(files.length, expect.length, 'find all files'); 10 | t.deepEqual(files, order(expect), 'matches'); 11 | } 12 | return files; 13 | } 14 | 15 | test('standard', async t => { 16 | t.plan(3); 17 | t.equal(typeof walk, 'function', 'consturctor is a typeof function'); 18 | t.equal(Array.isArray(await walk('.')), true, 'returns array'); 19 | t.equal(Array.isArray(await walk('')), true, 'empty string uses current dir .'); 20 | }); 21 | 22 | test('list', async t => { 23 | const expect = [ 24 | 'test/fixtures/a/b/c/file_c1.js', 25 | 'test/fixtures/a/b/c', 26 | 'test/fixtures/a/b/file_b1.js', 27 | 'test/fixtures/a/b', 28 | 'test/fixtures/a/file_a1.js', 29 | 'test/fixtures/a/file_a2.js', 30 | 'test/fixtures/a' 31 | ]; 32 | isMatch(t, 'test/fixtures/a', expect); 33 | t.end(); 34 | }); 35 | 36 | test('find', async t => { 37 | let match = await isMatch(t, 'test/fixtures'); 38 | t.equal(match.includes('test/fixtures/.hiddenfile'), true, 'find hidden files'); 39 | t.equal(match.includes('test/fixtures/.hiddendir/file.js'), true, 'find content in hidden dirs'); 40 | t.end(); 41 | }); 42 | 43 | test('option maxdepth', async t => { 44 | const expect = [ 45 | 'test/fixtures/.hiddendir', 46 | 'test/fixtures/.hiddenfile', 47 | 'test/fixtures/a', 48 | 'test/fixtures/file.js', 49 | 'test/fixtures/readme.md', 50 | 'test/fixtures' 51 | ]; 52 | await isMatch(t, 'test/fixtures', expect, { maxdepth:1 }); 53 | t.end(); 54 | }); 55 | 56 | test('option flatten', async t => { 57 | const expect = [ 58 | [ 59 | 'test/fixtures/a/b/c/file_c1.js', 60 | 'test/fixtures/a/b/c' 61 | ], 62 | 'test/fixtures/a/b/file_b1.js', 63 | 'test/fixtures/a/b' 64 | ] 65 | await isMatch(t, 'test/fixtures/a/b', expect, { flatten:false }); 66 | t.end(); 67 | }); 68 | 69 | test('option filesonly', async t => { 70 | const expect = [ 71 | 'test/fixtures/a/b/c/file_c1.js', 72 | 'test/fixtures/a/b/file_b1.js', 73 | ] 74 | await isMatch(t, 'test/fixtures/a/b', expect, { filesonly:true }); 75 | t.end(); 76 | }); 77 | 78 | test('option relative', async t => { 79 | const expect = [ 80 | 'test/fixtures/a/b/c/file_c1.js', 81 | 'test/fixtures/a/b/c', 82 | 'test/fixtures/a/b/file_b1.js', 83 | 'test/fixtures/a/b' 84 | ] 85 | 86 | const relative = await isMatch(t, 'test/fixtures/a/b', expect, { relative:true }); 87 | const abs = await isMatch(t, 'test/fixtures/a/b', null, { relative:false }); 88 | abs.forEach((file, i) => { 89 | t.equal(file.endsWith(relative[i]), true); 90 | }); 91 | t.end(); 92 | }); 93 | 94 | test('option cwd', async t => { 95 | const expect = [ 96 | 'a/b/c/file_c1.js', 97 | 'a/b/c', 98 | 'a/b/file_b1.js', 99 | 'a/b' 100 | ] 101 | await isMatch(t, 'a/b', expect, { cwd:'test/fixtures' }); 102 | t.end(); 103 | }); 104 | 105 | test('option cwd, relative backwards', async t => { 106 | const expect = [ 107 | 'c/file_c1.js', 108 | 'c', 109 | 'file_b1.js', 110 | '../../../b', 111 | '../file_a1.js', 112 | '../file_a2.js', 113 | '..' 114 | ] 115 | await isMatch(t, '../', expect, { cwd:'test/fixtures/a/b' }); 116 | t.end(); 117 | }); 118 | --------------------------------------------------------------------------------