4 A Node.js file system walker extracted from [fs-extra](https://github.com/jprichardson/node-fs-extra).
6 [![npm Package](https://img.shields.io/npm/v/klaw.svg?style=flat-square)](https://www.npmjs.org/package/klaw)
7 [![build status](https://api.travis-ci.org/jprichardson/node-klaw.svg)](http://travis-ci.org/jprichardson/node-klaw)
8 [![windows build status](https://ci.appveyor.com/api/projects/status/github/jprichardson/node-klaw?branch=master&svg=true)](https://ci.appveyor.com/project/jprichardson/node-klaw/branch/master)
10 <!-- [![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard) -->
11 <a href="http://standardjs.com"><img src="https://cdn.rawgit.com/feross/standard/master/sticker.svg" alt="Standard" width="100"></a>
22 `klaw` is `walk` backwards :p
28 ### klaw(directory, [options])
30 Returns a [Readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable) that iterates
31 through every file and directory starting with `dir` as the root. Every `read()` or `data` event
32 returns an object with two properties: `path` and `stats`. `path` is the full path of the file and
33 `stats` is an instance of [fs.Stats](https://nodejs.org/api/fs.html#fs_class_fs_stats).
35 - `directory`: The directory to recursively walk. Type `string`.
36 - `options`: [Readable stream options](https://nodejs.org/api/stream.html#stream_new_stream_readable_options) and
38 - `queueMethod` (`string`, default: `'shift'`): Either `'shift'` or `'pop'`. On `readdir()` array, call either `shift()` or `pop()`.
39 - `pathSorter` (`function`, default: `undefined`): Sorting [function for Arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).
40 - `fs` (`object`, default: `require('fs')`): Use this to hook into the `fs` methods or to use [`mock-fs`](https://github.com/tschaub/mock-fs)
41 - `filter` (`function`, default: `undefined`): Filtering [function for Arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
43 **Streams 1 (push) example:**
46 var klaw = require('klaw')
48 var items = [] // files, directories, symlinks, etc
50 .on('data', function (item) {
53 .on('end', function () {
54 console.dir(items) // => [ ... array of files]
58 **Streams 2 & 3 (pull) example:**
61 var klaw = require('klaw')
63 var items = [] // files, directories, symlinks, etc
65 .on('readable', function () {
67 while ((item = this.read())) {
71 .on('end', function () {
72 console.dir(items) // => [ ... array of files]
76 If you're not sure of the differences on Node.js streams 1, 2, 3 then I'd
77 recommend this resource as a good starting point: https://strongloop.com/strongblog/whats-new-io-js-beta-streams3/.
82 Listen for the `error` event.
87 var klaw = require('klaw')
89 .on('readable', function () {
91 while ((item = this.read())) {
92 // do something with the file
95 .on('error', function (err, item) {
96 console.log(err.message)
97 console.log(item.path) // the file the error occurred on
99 .on('end', function () {
100 console.dir(items) // => [ ... array of files]
106 ### Aggregation / Filtering / Executing Actions (Through Streams)
108 On many occasions you may want to filter files based upon size, extension, etc.
109 Or you may want to aggregate stats on certain file types. Or maybe you want to
110 perform an action on certain file types.
112 You should use the module [`through2`](https://www.npmjs.com/package/through2) to easily
117 npm i --save through2
120 **Example (skipping directories):**
123 var klaw = require('klaw')
124 var through2 = require('through2')
126 var excludeDirFilter = through2.obj(function (item, enc, next) {
127 if (!item.stats.isDirectory()) this.push(item)
131 var items = [] // files, directories, symlinks, etc
133 .pipe(excludeDirFilter)
134 .on('data', function (item) {
135 items.push(item.path)
137 .on('end', function () {
138 console.dir(items) // => [ ... array of files without directories]
142 **Example (ignore hidden directories):**
144 var klaw = require('klaw')
145 var path = require('path')
147 var filterFunc = function(item){
148 var basename = path.basename(item)
149 return basename === '.' || basename[0] !== '.'
152 klaw('/some/dir', { filter : filterFunc })
153 .on('data', function(item){
154 // only items of none hidden folders will reach here
159 **Example (totaling size of PNG files):**
162 var klaw = require('klaw')
163 var path = require('path')
164 var through2 = require('through2')
166 var totalPngsInBytes = 0
167 var aggregatePngSize = through2.obj(function (item, enc, next) {
168 if (path.extname(item.path) === '.png') {
169 totalPngsInBytes += item.stats.size
176 .pipe(aggregatePngSize)
177 .on('data', function (item) {
178 items.push(item.path)
180 .on('end', function () {
181 console.dir(totalPngsInBytes) // => total of all pngs (bytes)
186 **Example (deleting all .tmp files):**
189 var fs = require('fs')
190 var klaw = require('klaw')
191 var through2 = require('through2')
193 var deleteAction = through2.obj(function (item, enc, next) {
196 if (path.extname(item.path) === '.tmp') {
198 fs.unklink(item.path, next)
205 var deletedFiles = []
208 .on('data', function (item) {
209 if (!item.deleted) return
210 deletedFiles.push(item.path)
212 .on('end', function () {
213 console.dir(deletedFiles) // => all deleted files
217 You can even chain a bunch of these filters and aggregators together. By using
220 **Example (using multiple filters / aggregators):**
224 .pipe(filterCertainFiles)
225 .pipe(deleteSomeOtherFiles)
226 .on('end', function () {
227 console.log('all done!')
231 **Example passing (piping) through errors:**
233 Node.js does not `pipe()` errors. This means that the error on one stream, like
234 `klaw` will not pipe through to the next. If you want to do this, do the following:
237 var klaw = require('klaw')
238 var through2 = require('through2')
240 var excludeDirFilter = through2.obj(function (item, enc, next) {
241 if (!item.stats.isDirectory()) this.push(item)
245 var items = [] // files, directories, symlinks, etc
247 .on('error', function (err) { excludeDirFilter.emit('error', err) }) // forward the error on
248 .pipe(excludeDirFilter)
249 .on('data', function (item) {
250 items.push(item.path)
252 .on('end', function () {
253 console.dir(items) // => [ ... array of files without directories]
258 ### Searching Strategy
260 Pass in options for `queueMethod` and `pathSorter` to affect how the file system
261 is recursively iterated. See the code for more details, it's less than 50 lines :)
270 Copyright (c) 2015 [JP Richardson](https://github.com/jprichardson)