pax_global_header00006660000000000000000000000064120760302100014501gustar00rootroot0000000000000052 comment=a1893f2d2c72d7c973452f78673de28b1b658e42 fstream-ignore-0.0.6/000077500000000000000000000000001207603021000144265ustar00rootroot00000000000000fstream-ignore-0.0.6/.gitignore000066400000000000000000000000161207603021000164130ustar00rootroot00000000000000test/fixtures fstream-ignore-0.0.6/LICENSE000066400000000000000000000024361207603021000154400ustar00rootroot00000000000000Copyright (c) Isaac Z. Schlueter ("Author") All rights reserved. The BSD License Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. fstream-ignore-0.0.6/README.md000066400000000000000000000011551207603021000157070ustar00rootroot00000000000000# fstream-ignore A fstream DirReader that filters out files that match globs in `.ignore` files throughout the tree, like how git ignores files based on a `.gitignore` file. Here's an example: ```javascript var Ignore = require("fstream-ignore") Ignore({ path: __dirname , ignoreFiles: [".ignore", ".gitignore"] }) .on("child", function (c) { console.error(c.path.substr(c.root.path.length + 1)) }) .pipe(tar.Pack()) .pipe(fs.createWriteStream("foo.tar")) ``` This will tar up the files in __dirname into `foo.tar`, ignoring anything matched by the globs in any .iginore or .gitignore file. fstream-ignore-0.0.6/example/000077500000000000000000000000001207603021000160615ustar00rootroot00000000000000fstream-ignore-0.0.6/example/basic.js000066400000000000000000000005371207603021000175050ustar00rootroot00000000000000var Ignore = require("../") Ignore({ path: __dirname , ignoreFiles: [".ignore", ".gitignore"] }) .on("child", function (c) { console.error(c.path.substr(c.root.path.length + 1)) c.on("ignoreFile", onIgnoreFile) }) .on("ignoreFile", onIgnoreFile) function onIgnoreFile (e) { console.error("adding ignore file", e.path) } fstream-ignore-0.0.6/ignore.js000066400000000000000000000164571207603021000162640ustar00rootroot00000000000000// Essentially, this is a fstream.DirReader class, but with a // bit of special logic to read the specified sort of ignore files, // and a filter that prevents it from picking up anything excluded // by those files. var Minimatch = require("minimatch").Minimatch , fstream = require("fstream") , DirReader = fstream.DirReader , inherits = require("inherits") , path = require("path") , fs = require("fs") module.exports = IgnoreReader inherits(IgnoreReader, DirReader) function IgnoreReader (props) { if (!(this instanceof IgnoreReader)) { return new IgnoreReader(props) } // must be a Directory type if (typeof props === "string") { props = { path: path.resolve(props) } } props.type = "Directory" props.Directory = true if (!props.ignoreFiles) props.ignoreFiles = [".ignore"] this.ignoreFiles = props.ignoreFiles this.ignoreRules = null // ensure that .ignore files always show up at the top of the list // that way, they can be read before proceeding to handle other // entries in that same folder if (props.sort) { this._sort = props.sort === "alpha" ? alphasort : props.sort props.sort = null } this.on("entries", function () { // if there are any ignore files in the list, then // pause and add them. // then, filter the list based on our ignoreRules var hasIg = this.entries.some(this.isIgnoreFile, this) if (!hasIg) return this.filterEntries() this.addIgnoreFiles() }) // we filter entries before we know what they are. // however, directories have to be re-tested against // rules with a "/" appended, because "a/b/" will only // match if "a/b" is a dir, and not otherwise. this.on("_entryStat", function (entry, props) { var t = entry.basename if (!this.applyIgnores(entry.basename, entry.type === "Directory", entry)) { entry.abort() } }.bind(this)) DirReader.call(this, props) } IgnoreReader.prototype.addIgnoreFiles = function () { if (this._paused) { this.once("resume", this.addIgnoreFiles) return } if (this._ignoreFilesAdded) return this._ignoreFilesAdded = true var newIg = this.entries.filter(this.isIgnoreFile, this) , count = newIg.length , errState = null if (!count) return this.pause() var then = function then (er) { if (errState) return if (er) return this.emit("error", errState = er) if (-- count === 0) { this.filterEntries() this.resume() } }.bind(this) newIg.forEach(function (ig) { this.addIgnoreFile(ig, then) }, this) } IgnoreReader.prototype.isIgnoreFile = function (e) { return e !== "." && e !== ".." && -1 !== this.ignoreFiles.indexOf(e) } IgnoreReader.prototype.getChildProps = function (stat) { var props = DirReader.prototype.getChildProps.call(this, stat) props.ignoreFiles = this.ignoreFiles // Directories have to be read as IgnoreReaders // otherwise fstream.Reader will create a DirReader instead. if (stat.isDirectory()) { props.type = this.constructor } return props } IgnoreReader.prototype.addIgnoreFile = function (e, cb) { // read the file, and then call addIgnoreRules // if there's an error, then tell the cb about it. var ig = path.resolve(this.path, e) fs.readFile(ig, function (er, data) { if (er) return cb(er) this.emit("ignoreFile", e, data) var rules = this.readRules(data, e) this.addIgnoreRules(rules, e) cb() }.bind(this)) } IgnoreReader.prototype.readRules = function (buf, e) { return buf.toString().split(/\r?\n/) } // Override this to do fancier things, like read the // "files" array from a package.json file or something. IgnoreReader.prototype.addIgnoreRules = function (set, e) { // filter out anything obvious set = set.filter(function (s) { s = s.trim() return s && !s.match(/^#/) }) // no rules to add! if (!set.length) return // now get a minimatch object for each one of these. // Note that we need to allow dot files by default, and // not switch the meaning of their exclusion var mmopt = { matchBase: true, dot: true, flipNegate: true } , mm = set.map(function (s) { var m = new Minimatch(s, mmopt) m.ignoreFile = e return m }) if (!this.ignoreRules) this.ignoreRules = [] this.ignoreRules.push.apply(this.ignoreRules, mm) } IgnoreReader.prototype.filterEntries = function () { // this exclusion is at the point where we know the list of // entries in the dir, but don't know what they are. since // some of them *might* be directories, we have to run the // match in dir-mode as well, so that we'll pick up partials // of files that will be included later. Anything included // at this point will be checked again later once we know // what it is. this.entries = this.entries.filter(function (entry) { // at this point, we don't know if it's a dir or not. return this.applyIgnores(entry) || this.applyIgnores(entry, true) }, this) } IgnoreReader.prototype.applyIgnores = function (entry, partial, obj) { var included = true // this = /a/b/c // entry = d // parent /a/b sees c/d if (this.parent && this.parent.applyIgnores) { var pt = this.basename + "/" + entry included = this.parent.applyIgnores(pt, partial) } // Negated Rules // Since we're *ignoring* things here, negating means that a file // is re-included, if it would have been excluded by a previous // rule. So, negated rules are only relevant if the file // has been excluded. // // Similarly, if a file has been excluded, then there's no point // trying it against rules that have already been applied // // We're using the "flipnegate" flag here, which tells minimatch // to set the "negate" for our information, but still report // whether the core pattern was a hit or a miss. if (!this.ignoreRules) { return included } this.ignoreRules.forEach(function (rule) { // negation means inclusion if (rule.negate && included || !rule.negate && !included) { // unnecessary return } // first, match against /foo/bar var match = rule.match("/" + entry) if (!match) { // try with the leading / trimmed off the test // eg: foo/bar instead of /foo/bar match = rule.match(entry) } // if the entry is a directory, then it will match // with a trailing slash. eg: /foo/bar/ or foo/bar/ if (!match && partial) { match = rule.match("/" + entry + "/") || rule.match(entry + "/") } // When including a file with a negated rule, it's // relevant if a directory partially matches, since // it may then match a file within it. // Eg, if you ignore /a, but !/a/b/c if (!match && rule.negate && partial) { match = rule.match("/" + entry, true) || rule.match(entry, true) } if (match) { included = rule.negate } }, this) return included } IgnoreReader.prototype.sort = function (a, b) { var aig = this.ignoreFiles.indexOf(a) !== -1 , big = this.ignoreFiles.indexOf(b) !== -1 if (aig && !big) return -1 if (big && !aig) return 1 return this._sort(a, b) } IgnoreReader.prototype._sort = function (a, b) { return 0 } function alphasort (a, b) { return a === b ? 0 : a.toLowerCase() > b.toLowerCase() ? 1 : a.toLowerCase() < b.toLowerCase() ? -1 : a > b ? 1 : -1 } fstream-ignore-0.0.6/package.json000066400000000000000000000010501207603021000167100ustar00rootroot00000000000000{ "author": "Isaac Z. Schlueter (http://blog.izs.me/)", "name": "fstream-ignore", "description": "A thing for ignoring files based on globs", "version": "0.0.6", "repository": { "type": "git", "url": "git://github.com/isaacs/fstream-ignore.git" }, "main": "ignore.js", "scripts": { "test": "tap test/*.js" }, "dependencies": { "minimatch": "~0.2.0", "fstream": "~0.1.17", "inherits": "~1.0.0" }, "devDependencies": { "tap": "", "rimraf": "", "mkdirp": "" }, "license": "BSD" } fstream-ignore-0.0.6/test/000077500000000000000000000000001207603021000154055ustar00rootroot00000000000000fstream-ignore-0.0.6/test/.gitignore000066400000000000000000000000041207603021000173670ustar00rootroot00000000000000*/a fstream-ignore-0.0.6/test/.ignore000066400000000000000000000000221207603021000166630ustar00rootroot00000000000000.gitignore .*.swp fstream-ignore-0.0.6/test/00-setup.js000066400000000000000000000033751207603021000173300ustar00rootroot00000000000000// The test fixtures work like this: // These dirs are all created: {a,b,c}/{a,b,c}/{a,b,c}/ // in each one, these files are created: // {.,}{a,b,c}{a,b,c}{a,b,c} // // So, there'll be a/b/c/abc, a/b/c/aba, etc., and dot-versions of each. // // Each test then writes their own ignore file rules for their purposes, // and is responsible for removing them afterwards. var mkdirp = require("mkdirp") var path = require("path") var i = 0 var tap = require("tap") var fs = require("fs") var rimraf = require("rimraf") var fixtures = path.resolve(__dirname, "fixtures") var chars = ['a', 'b', 'c'] var dirs = [] for (var i = 0; i < 3; i ++) { for (var j = 0; j < 3; j ++) { for (var k = 0; k < 3; k ++) { dirs.push(chars[i] + '/' + chars[j] + '/' + chars[k]) } } } var files = [] for (var i = 0; i < 3; i ++) { for (var j = 0; j < 3; j ++) { for (var k = 0; k < 3; k ++) { files.push(chars[i] + chars[j] + chars[k]) files.push('.' + chars[i] + chars[j] + chars[k]) } } } tap.test("remove fixtures", function (t) { rimraf(path.resolve(__dirname, "fixtures"), function (er) { t.ifError(er, "remove fixtures") t.end() }) }) tap.test("create fixtures", function (t) { dirs.forEach(function (dir) { dir = path.resolve(fixtures, dir) t.test("mkdir "+dir, function (t) { mkdirp(dir, function (er) { t.ifError(er, "mkdir "+dir) if (er) return t.end() files.forEach(function (file) { file = path.resolve(dir, file) t.test("writeFile "+file, function (t) { fs.writeFile(file, path.basename(file), function (er) { t.ifError(er, "writing "+file) t.end() }) }) }) t.end() }) }) }) t.end() }) fstream-ignore-0.0.6/test/basic.js000066400000000000000000000012771207603021000170330ustar00rootroot00000000000000var IgnoreFile = require("../") // set the ignores just for this test var c = require("./common.js") c.ignores({ "a/.basic-ignore": ["b/", "aca"] }) // the files that we expect to not see var notAllowed = [ /^\/a\/b\/.*/ , /^\/a\/.*\/aca$/ ] require("tap").test("basic ignore rules", function (t) { t.pass("start") IgnoreFile({ path: __dirname + "/fixtures" , ignoreFiles: [".basic-ignore"] }) .on("ignoreFile", function (e) { console.error("ignore file!", e) }) .on("child", function (e) { var p = e.path.substr(e.root.path.length) notAllowed.forEach(function (na) { t.dissimilar(p, na) }) }) .on("close", t.end.bind(t)) }) fstream-ignore-0.0.6/test/common.js000066400000000000000000000015771207603021000172450ustar00rootroot00000000000000if (require.main === module) { console.log("0..1") console.log("ok 1 trivial pass") return } var fs = require("fs") var path = require("path") var rimraf = require("rimraf") exports.ignores = ignores exports.writeIgnoreFile = writeIgnoreFile exports.writeIgnores = writeIgnores exports.clearIgnores = clearIgnores function writeIgnoreFile (file, rules) { file = path.resolve(__dirname, "fixtures", file) if (Array.isArray(rules)) { rules = rules.join("\n") } fs.writeFileSync(file, rules) console.error(file, rules) } function writeIgnores (set) { Object.keys(set).forEach(function (f) { writeIgnoreFile(f, set[f]) }) } function clearIgnores (set) { Object.keys(set).forEach(function (file) { fs.unlinkSync(path.resolve(__dirname, "fixtures", file)) }) } function ignores (set) { writeIgnores(set) process.on("exit", clearIgnores.bind(null, set)) } fstream-ignore-0.0.6/test/ignore-most.js000066400000000000000000000017201207603021000202060ustar00rootroot00000000000000// ignore most things var IgnoreFile = require("../") // set the ignores just for this test var c = require("./common.js") c.ignores({ ".ignore": ["*", "!a/b/c/.abc", "!/c/b/a/cba"] }) // the only files we expect to see var expected = [ "/a/b/c/.abc" , "/a" , "/a/b" , "/a/b/c" , "/c/b/a/cba" , "/c" , "/c/b" , "/c/b/a" ] require("tap").test("basic ignore rules", function (t) { t.pass("start") IgnoreFile({ path: __dirname + "/fixtures" , ignoreFiles: [".ignore"] }) .on("ignoreFile", function (e) { console.error("ignore file!", e) }) .on("child", function (e) { var p = e.path.substr(e.root.path.length) var i = expected.indexOf(p) if (i === -1) { t.fail("unexpected file found", {file: p}) } else { t.pass(p) expected.splice(i, 1) } }) .on("close", function () { t.notOk(expected.length, "all expected files should be seen") t.end() }) }) fstream-ignore-0.0.6/test/nested-ignores.js000066400000000000000000000024211207603021000206700ustar00rootroot00000000000000// ignore most things var IgnoreFile = require("../") // set the ignores just for this test var c = require("./common.js") c.ignores( { ".ignore": ["*", "a", "c", "!a/b/c/.abc", "!/c/b/a/cba"] , "a/.ignore": [ "!*", ".ignore" ] // unignore everything , "a/a/.ignore": [ "*" ] // re-ignore everything , "a/b/.ignore": [ "*", "!/c/.abc" ] // original unignore , "a/c/.ignore": [ "*" ] // ignore everything again , "c/b/a/.ignore": [ "!cba", "!.cba", "!/a{bc,cb}" ] }) // the only files we expect to see var expected = [ "/a" , "/a/a" , "/a/b" , "/a/b/c" , "/a/b/c/.abc" , "/a/c" , "/c" , "/c/b" , "/c/b/a" , "/c/b/a/cba" , "/c/b/a/.cba" , "/c/b/a/abc" , "/c/b/a/acb" ] require("tap").test("basic ignore rules", function (t) { t.pass("start") IgnoreFile({ path: __dirname + "/fixtures" , ignoreFiles: [".ignore"] }) .on("child", function (e) { var p = e.path.substr(e.root.path.length) var i = expected.indexOf(p) if (i === -1) { console.log("not ok "+p) t.fail("unexpected file found", {found: p}) } else { t.pass(p) expected.splice(i, 1) } }) .on("close", function () { t.deepEqual(expected, [], "all expected files should be seen") t.end() }) }) fstream-ignore-0.0.6/test/unignore-child.js000066400000000000000000000016041207603021000206530ustar00rootroot00000000000000// ignore most things var IgnoreFile = require("../") // set the ignores just for this test var c = require("./common.js") c.ignores({ ".ignore": ["*", "a", "c", "!a/b/c/.abc", "!/c/b/a/cba"] }) // the only files we expect to see var expected = [ "/a/b/c/.abc" , "/a" , "/a/b" , "/a/b/c" , "/c/b/a/cba" , "/c" , "/c/b" , "/c/b/a" ] require("tap").test("basic ignore rules", function (t) { t.pass("start") IgnoreFile({ path: __dirname + "/fixtures" , ignoreFiles: [".ignore"] }) .on("child", function (e) { var p = e.path.substr(e.root.path.length) var i = expected.indexOf(p) if (i === -1) { t.fail("unexpected file found", {f: p}) } else { t.pass(p) expected.splice(i, 1) } }) .on("close", function () { t.notOk(expected.length, "all expected files should be seen") t.end() }) }) fstream-ignore-0.0.6/test/zz-cleanup.js000066400000000000000000000003621207603021000200340ustar00rootroot00000000000000var tap = require("tap") , rimraf = require("rimraf") , path = require("path") tap.test("remove fixtures", function (t) { rimraf(path.resolve(__dirname, "fixtures"), function (er) { t.ifError(er, "remove fixtures") t.end() }) })