# {%= name %} {%= badge("fury") %} > {%= description %} ## Usage ```js var glob = require('{%= name %}')({ gitignore: true }); var files = glob.readdirSync('**/*.js'); ``` [Run actual examples](./examples/): Jump to docs sections: - [glob.readdir](#async) - [glob.readPromise](#promise) - [glob.readStream](#stream) - [glob.readdirSync](#sync) ## Table of contents ## Install {%= include("install-npm", {save: true}) %} ## Usage **Params** All "read" methods take a glob pattern and an `options` object. - `pattern` **{String}**: Glob pattern to use for matching. (multiple pattern support is planned) - `options` **{Object}**: Options for `glob-fs` or middleware. **Examples:** ```js // sync var files = glob.readdirSync('*.js', {}); // async glob.readdir('*.js', function(err, files) { console.log(files); }); // stream glob.readdirStream('*.js', {}) .on('data', function(file) { console.log(file); }); // promise glob.readdirPromise('*.js') .then(function(files) { console.log(file); }); ``` ## API {%= apidocs("./lib/readers.js") %} {%= apidocs("index.js") %} ## Middleware glob-fs uses middleware to add file matching and exclusion capabilities, or other features that may or may not eventually become core functionality. **What is a middleware?** A middleware is a function that "processes" files as they're read from the file system by glob-fs. Additionally, middleware can: - be chained - `include` or `exclude` a file based on some condition, like whether or not one of its properties matches a regex or glob pattern. - determine whether or not to continue recursing in a specific directory - modifying an existing property to the `file` object - add a new property to the `file` object ### Middleware examples **Ignoring files** In the following example, `notemp` is a complete and functional middleware for excluding any filepath that has the substring `temp`: ```js var glob = require('glob-fs')(); function notemp(file) { if (/temp/.test(file.path)) { file.exclude = true; } return file; } glob.use(notemp) .readdirStream('**/*.js') .on('data', function(file) { console.log(file.relative); }); ``` **Matching** Pattern matching is done by default in glob-fs, but you get disable the built-in matchers or get more specific by adding a middleware that uses [micromatch][] or [minimatch][] for matching files. ```js var glob = require('glob-fs')({ gitignore: true }); var mm = require('micromatch'); glob.use(function(file) { if (mm.isMatch(file.relative, 'vendor/**')) file.exclude = true; return file; }) .readdirStream('**/*.js') .on('data', function(file) { console.log(file.relative); }); ``` **recursion** Here is how a middleware might determine whether or not to recurse based on a certain pattern: ```js var glob = require('glob-fs')(); // this specific check is already done by glob-fs, it's just used here as an example function recurse(file) { // `file.pattern` is an object with a `glob` (string) property file.recurse = file.pattern.glob.indexOf('**') !== -1; return file; } // use the middleware glob.use(recurse) .readdir('**/*.js', function(err, files) { console.log(files); }); ``` **Built-in middleware** Currently glob-fs includes and runs the following middleware automatically: {%= related(mm(Object.keys(dependencies), "glob-fs*")) %} **Disabling built-ins** To disable built-in middleware and prevent them from running, pass `builtins: false` on the global options. This will disable **all built-in middleware**. Example: ```js var glob = require('glob-fs')({builtins: false}); ``` To disable a specific middleware from running, you can usually pass the name of the middleware on the options, like `dotfiles: false`, but it's best to check the readme of that middleware for specifics. ### Middleware conventions - **Naming**: any middleware published to npm should be prefixed with `glob-fs-`, as in: `glob-fs-dotfiles`. - **Keywords**: please add `glob-fs` to the keywords array in package.json - **Options**: all middleware should return a function that takes an `options` object, as in the [Middleware Example](#middleware-example) - **Return `file`**: all middleware should return the `file` object after processing. ### Advice for middleware authors - A middleware should only do one specific thing. - Multiple middleware libs can be bundled together to create a single middleware. - Pattern matching should be extremely specific. Don't force downstream middleware to reverse your mistakes. - As mentioned in the [middleware conventions](#middleware-conventions) section, **always return the `file` object**. - A single conditional should only set `file.exclude` to `true`, or `file.include` to `true`, never both. - It's completely okay to check `this.options` - Middleware modules should be fully documented. ## Globbing examples Note that the `gitignore` option is already `true` by default, it's just shown here as a placeholder for how options may be defined. ### async ```js var glob = require('{%= name %}')({ gitignore: true }); glob.readdir('**/*.js', function(err, files) { console.log(files); }); ``` ### promise ```js var glob = require('{%= name %}')({ gitignore: true }); glob.readdirPromise('**/*') .then(function (files) { console.log(files); }); ``` ### stream ```js var glob = require('{%= name %}')({ gitignore: true }); glob.readdirStream('**/*') .on('data', function (file) { console.log(file.path); }) ``` ### sync ```js var glob = require('{%= name %}')({ gitignore: true }); var files = glob.readdirSync('**/*.js'); console.log(files); ``` ## Events _(WIP)_ The following events are emitted with all "read" methods: - `read`: emitted immediately before an iterator calls the first middleware. - `include`: emits a `file` object when it's matched - `exclude`: emits a `file` object when it's ignored/excluded - `file`: emits a `file` object when the iterator pushes it into the results array. Only applies to `sync`, `async` and `promise`. - `dir`: emits a `file` object when the iterator finds a directory - `end` when the iterator is finished reading - `error` on errors ### Event examples **async** ```js var glob = require('..')({ gitignore: true }); glob.on('dir', function (file) { console.log(file); }); glob.readdir('**/*.js', function (err, files) { if (err) return console.error(err); console.log(files.length); }); ``` **promise** ```js var glob = require('{%= name %}')({ gitignore: true }); glob.on('include', function (file) { console.log('including:', file.path); }); glob.on('exclude', function (file) { console.log('excluding:', file.path); }); glob.readdirPromise('**/*'); ``` **sync** Also has an example of a custom event, emitted from a middleware: ```js var glob = require('{%= name %}')({ gitignore: true }) .use(function (file) { if (/\.js$/.test(file.path)) { // custom event this.emit('js', file); } return file; }); glob.on('js', function (file) { console.log('js file:', file.path); }); glob.on('exclude', function (file) { console.log('excluded:', i.excludes++); }); glob.on('include', function (file) { console.log('included:', i.includes++) }); glob.on('end', function () { console.log('total files:', this.files.length); }); glob.readdirSync('**/*.js'); ``` **stream** ```js var glob = require('{%= name %}')({ gitignore: true }) glob.readdirStream('**/*') .on('data', function (file) { console.log(file.path) }) .on('error', console.error) .on('end', function () { console.log('end'); }); ``` ## FAQ - when files are read from the file system, an object is created to keep a record of the file's `path`, `dirname`, and fs `stat` object and other pertinent information that makes it easier to make decisions about inclusion and exclusion later on. - `file` objects are decorated with a `parse` method that is used to calculate the `file.relative` and `file.absolute` properties. - the `file.parse()` method is called in the iterator, right after the call to `fs.stats` and just before the call to the middleware handler (`.handle()`). This ensures that all middleware have access to necessary path information. - `file.relative` is the file path that's actually pushed into the `files` array that is ultimately returned. - `file.relative` is calculated using `path.relative(file.path, cwd)`, where `cwd` is passed on the options (globally, or on a middleware), and `file.path` is typically the absolute, actual file path to the file being globbed. ## TODO **middleware** * [x] middleware * [x] middleware handler * [ ] externalize middleware to modules (started, [prs welcome!](#contributing)) **events** * [x] events **tests** * [x] unit tests (need to be moved) **iterators** * [x] sync iterator * [x] async iterator * [x] stream iterator * [x] promise iterator **read methods** * [x] glob.readdir (async) * [x] glob.readdirSync * [x] glob.readdirStream * [x] glob.readdirPromise **patterns** * [ ] Multiple pattern support. will need to change pattern handling, middleware handling. this is POC currently * [ ] Negation patterns (might not do this, since it can be handled in middleware) * [ ] matching method, memoized/cached/bound to a glob pattern or patterns, so it can be reused without having to recompile the regex. **other** * [ ] clean up `./lib` * [ ] comparsion to [node-glob][] ## Community middleware _(Add your project to the [.verb.md](./.verb.md) template do a PR!)_ {%= related(['glob-fs-dotfiles', 'glob-fs-gitignore']) %} ## Related projects {%= related(['micromatch', 'braces', 'fill-range', 'is-glob']) %} ## Running tests {%= include("tests") %} ## Contributing {%= include("contributing") %} ## Author {%= include("author") %} ## License {%= copyright() %} {%= license() %} *** {%= include("footer") %} {%= reflinks(['verb', 'glob', 'minimatch']) %}