pax_global_header00006660000000000000000000000064127167014320014515gustar00rootroot0000000000000052 comment=1438a1a2c25fee71327c36c86de787a14a53ffd4 fstream-ignore-1.0.5/000077500000000000000000000000001271670143200144425ustar00rootroot00000000000000fstream-ignore-1.0.5/.gitignore000066400000000000000000000000631271670143200164310ustar00rootroot00000000000000test/fixtures .nyc_output/ coverage/ node_modules/ fstream-ignore-1.0.5/.travis.yml000066400000000000000000000001501271670143200165470ustar00rootroot00000000000000sudo: false language: node_js node_js: - '0.10' - '0.12' - '4' - '5' - '6' script: "npm test" fstream-ignore-1.0.5/LICENSE000066400000000000000000000013751271670143200154550ustar00rootroot00000000000000The ISC License Copyright (c) Isaac Z. Schlueter and Contributors 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. fstream-ignore-1.0.5/README.md000066400000000000000000000011551271670143200157230ustar00rootroot00000000000000# 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-1.0.5/example/000077500000000000000000000000001271670143200160755ustar00rootroot00000000000000fstream-ignore-1.0.5/example/basic.js000066400000000000000000000005371271670143200175210ustar00rootroot00000000000000var 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-1.0.5/ignore.js000066400000000000000000000165141271670143200162720ustar00rootroot00000000000000// 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 (er) { if (errState) return if (er) return this.emit("error", errState = er) if (-- count === 0) { this.filterEntries() this.resume() } else { this.addIgnoreFile(newIg[newIg.length - count], then) } }.bind(this) this.addIgnoreFile(newIg[0], then) } 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-1.0.5/package.json000066400000000000000000000011201271670143200167220ustar00rootroot00000000000000{ "author": "Isaac Z. Schlueter (http://blog.izs.me/)", "name": "fstream-ignore", "description": "A thing for ignoring files based on globs", "version": "1.0.5", "repository": { "type": "git", "url": "git://github.com/isaacs/fstream-ignore.git" }, "main": "ignore.js", "scripts": { "test": "tap test/*.js --cov" }, "dependencies": { "fstream": "^1.0.0", "inherits": "2", "minimatch": "^3.0.0" }, "devDependencies": { "mkdirp": "", "rimraf": "", "tap": "^5.7.1" }, "license": "ISC", "files": [ "ignore.js" ] } fstream-ignore-1.0.5/test/000077500000000000000000000000001271670143200154215ustar00rootroot00000000000000fstream-ignore-1.0.5/test/.gitignore000066400000000000000000000000041271670143200174030ustar00rootroot00000000000000*/a fstream-ignore-1.0.5/test/.ignore000066400000000000000000000000221271670143200166770ustar00rootroot00000000000000.gitignore .*.swp fstream-ignore-1.0.5/test/00-setup.js000066400000000000000000000033751271670143200173440ustar00rootroot00000000000000// 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-1.0.5/test/basic.js000066400000000000000000000012771271670143200170470ustar00rootroot00000000000000var 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-1.0.5/test/common.js000066400000000000000000000015771271670143200172610ustar00rootroot00000000000000if (require.main === module) { console.log("1..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-1.0.5/test/ignore-most.js000066400000000000000000000017771271670143200202360ustar00rootroot00000000000000// ignore most things var IgnoreFile = require("../") , path = require('path') // 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" ].map(path.normalize) 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-1.0.5/test/nested-ignores.js000066400000000000000000000025001271670143200207020ustar00rootroot00000000000000// ignore most things var IgnoreFile = require("../") , path = require('path') // 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" ].map(path.normalize) 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-1.0.5/test/read-file-order.js000066400000000000000000000040461271670143200207240ustar00rootroot00000000000000var IgnoreFile = require("../") , fs = require('fs') , path = require('path') // set the ignores just for this test var c = require("./common.js") c.ignores({ ".gitignore": ["a/b/c/abc"] }) c.ignores({ ".ignore": ["*", "!a/b/c/abc"] }) // the only files we expect to see var expected = [ "/a" , "/a/b" , "/a/b/c" , "/a/b/c/abc" ].map(path.normalize) var originalReadFile = fs.readFile , parallelCount = 0 , firstCall // Overwrite fs.readFile so that when .gitignore and .ignore are read in // parallel, .ignore will always be read first. fs.readFile = function (filename, options, callback) { if (typeof options === 'function') { callback = options options = false } parallelCount++ process.nextTick(function () { if (parallelCount > 1) { if (!firstCall) { return firstCall = function (cb) { originalReadFile(filename, options, function (err, data) { callback(err, data) if (cb) cb() }) } } if (filename.indexOf('.gitignore') !== -1) { firstCall(function () { originalReadFile(filename, options, callback) }) } else { originalReadFile(filename, options, function (err, data) { callback(err, data) firstCall() }) } } else { originalReadFile(filename, options, callback) parallelCount = 0 } }) } require("tap").test("read file order", function (t) { t.pass("start") IgnoreFile({ path: __dirname + "/fixtures" , ignoreFiles: [".gitignore", ".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", {f: p}) } else { t.pass(p) expected.splice(i, 1) } }) .on("close", function () { fs.readFile = originalReadFile t.notOk(expected.length, "all expected files should be seen") t.end() }) }) fstream-ignore-1.0.5/test/unignore-child.js000066400000000000000000000016631271670143200206740ustar00rootroot00000000000000// ignore most things var IgnoreFile = require("../") , path = require('path') // 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" ].map(path.normalize) 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-1.0.5/test/zz-cleanup.js000066400000000000000000000003621271670143200200500ustar00rootroot00000000000000var 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() }) })