210 lines
4.9 KiB
JavaScript
210 lines
4.9 KiB
JavaScript
|
'use strict';
|
||
|
|
||
|
var through2 = require('through2');
|
||
|
var Combine = require('ordered-read-streams');
|
||
|
var unique = require('unique-stream');
|
||
|
|
||
|
var glob = require('glob');
|
||
|
var micromatch = require('micromatch');
|
||
|
var resolveGlob = require('to-absolute-glob');
|
||
|
var globParent = require('glob-parent');
|
||
|
var path = require('path');
|
||
|
var extend = require('extend');
|
||
|
var sepRe = (process.platform === 'win32' ? /[\/\\]/ : /\/+/);
|
||
|
|
||
|
var gs = {
|
||
|
// Creates a stream for a single glob or filter
|
||
|
createStream: function(ourGlob, negatives, opt) {
|
||
|
|
||
|
var ourOpt = extend({}, opt);
|
||
|
delete ourOpt.root;
|
||
|
|
||
|
// Extract base path from glob
|
||
|
var basePath = ourOpt.base || getBasePath(ourGlob, opt);
|
||
|
|
||
|
// Remove path relativity to make globs make sense
|
||
|
ourGlob = resolveGlob(ourGlob, opt);
|
||
|
|
||
|
// Create globbing stuff
|
||
|
var globber = new glob.Glob(ourGlob, ourOpt);
|
||
|
|
||
|
// Create stream and map events from globber to it
|
||
|
var stream = through2.obj(opt,
|
||
|
negatives.length ? filterNegatives : undefined);
|
||
|
|
||
|
var found = false;
|
||
|
|
||
|
globber.on('error', stream.emit.bind(stream, 'error'));
|
||
|
globber.once('end', function() {
|
||
|
if (opt.allowEmpty !== true && !found && globIsSingular(globber)) {
|
||
|
stream.emit('error',
|
||
|
new Error('File not found with singular glob: ' + ourGlob));
|
||
|
}
|
||
|
|
||
|
stream.end();
|
||
|
});
|
||
|
globber.on('match', function(filename) {
|
||
|
found = true;
|
||
|
|
||
|
stream.write({
|
||
|
cwd: opt.cwd,
|
||
|
base: basePath,
|
||
|
path: path.normalize(filename),
|
||
|
});
|
||
|
});
|
||
|
|
||
|
return stream;
|
||
|
|
||
|
function filterNegatives(filename, enc, cb) {
|
||
|
var matcha = isMatch.bind(null, filename);
|
||
|
if (negatives.every(matcha)) {
|
||
|
cb(null, filename); // Pass
|
||
|
} else {
|
||
|
cb(); // Ignore
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
// Creates a stream for multiple globs or filters
|
||
|
create: function(globs, opt) {
|
||
|
if (!opt) {
|
||
|
opt = {};
|
||
|
}
|
||
|
if (typeof opt.cwd !== 'string') {
|
||
|
opt.cwd = process.cwd();
|
||
|
}
|
||
|
if (typeof opt.dot !== 'boolean') {
|
||
|
opt.dot = false;
|
||
|
}
|
||
|
if (typeof opt.silent !== 'boolean') {
|
||
|
opt.silent = true;
|
||
|
}
|
||
|
if (typeof opt.nonull !== 'boolean') {
|
||
|
opt.nonull = false;
|
||
|
}
|
||
|
if (typeof opt.cwdbase !== 'boolean') {
|
||
|
opt.cwdbase = false;
|
||
|
}
|
||
|
if (opt.cwdbase) {
|
||
|
opt.base = opt.cwd;
|
||
|
}
|
||
|
|
||
|
// Only one glob no need to aggregate
|
||
|
if (!Array.isArray(globs)) {
|
||
|
globs = [globs];
|
||
|
}
|
||
|
|
||
|
var positives = [];
|
||
|
var negatives = [];
|
||
|
|
||
|
var ourOpt = extend({}, opt);
|
||
|
delete ourOpt.root;
|
||
|
|
||
|
globs.forEach(function(glob, index) {
|
||
|
if (typeof glob !== 'string' && !(glob instanceof RegExp)) {
|
||
|
throw new Error('Invalid glob at index ' + index);
|
||
|
}
|
||
|
|
||
|
var globArray = isNegative(glob) ? negatives : positives;
|
||
|
|
||
|
// Create Minimatch instances for negative glob patterns
|
||
|
if (globArray === negatives && typeof glob === 'string') {
|
||
|
var ourGlob = resolveGlob(glob, opt);
|
||
|
glob = micromatch.matcher(ourGlob, ourOpt);
|
||
|
}
|
||
|
|
||
|
globArray.push({
|
||
|
index: index,
|
||
|
glob: glob,
|
||
|
});
|
||
|
});
|
||
|
|
||
|
if (positives.length === 0) {
|
||
|
throw new Error('Missing positive glob');
|
||
|
}
|
||
|
|
||
|
// Only one positive glob no need to aggregate
|
||
|
if (positives.length === 1) {
|
||
|
return streamFromPositive(positives[0]);
|
||
|
}
|
||
|
|
||
|
// Create all individual streams
|
||
|
var streams = positives.map(streamFromPositive);
|
||
|
|
||
|
// Then just pipe them to a single unique stream and return it
|
||
|
var aggregate = new Combine(streams);
|
||
|
var uniqueStream = unique('path');
|
||
|
var returnStream = aggregate.pipe(uniqueStream);
|
||
|
|
||
|
aggregate.on('error', function(err) {
|
||
|
returnStream.emit('error', err);
|
||
|
});
|
||
|
|
||
|
return returnStream;
|
||
|
|
||
|
function streamFromPositive(positive) {
|
||
|
var negativeGlobs = negatives.filter(indexGreaterThan(positive.index))
|
||
|
.map(toGlob);
|
||
|
return gs.createStream(positive.glob, negativeGlobs, opt);
|
||
|
}
|
||
|
},
|
||
|
};
|
||
|
|
||
|
function isMatch(file, matcher) {
|
||
|
if (typeof matcher === 'function') {
|
||
|
return matcher(file.path);
|
||
|
}
|
||
|
if (matcher instanceof RegExp) {
|
||
|
return matcher.test(file.path);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function isNegative(pattern) {
|
||
|
if (typeof pattern === 'string') {
|
||
|
return pattern[0] === '!';
|
||
|
}
|
||
|
if (pattern instanceof RegExp) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function indexGreaterThan(index) {
|
||
|
return function(obj) {
|
||
|
return obj.index > index;
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function toGlob(obj) {
|
||
|
return obj.glob;
|
||
|
}
|
||
|
|
||
|
function globIsSingular(glob) {
|
||
|
var globSet = glob.minimatch.set;
|
||
|
|
||
|
if (globSet.length !== 1) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return globSet[0].every(function isString(value) {
|
||
|
return typeof value === 'string';
|
||
|
});
|
||
|
}
|
||
|
|
||
|
function getBasePath(ourGlob, opt) {
|
||
|
var basePath;
|
||
|
var parent = globParent(ourGlob);
|
||
|
|
||
|
if (parent === '/' && opt && opt.root) {
|
||
|
basePath = path.normalize(opt.root);
|
||
|
} else {
|
||
|
basePath = resolveGlob(parent, opt);
|
||
|
}
|
||
|
|
||
|
if (!sepRe.test(basePath.charAt(basePath.length - 1))) {
|
||
|
basePath += path.sep;
|
||
|
}
|
||
|
return basePath;
|
||
|
}
|
||
|
|
||
|
module.exports = gs;
|