Skip to content

Commit 3521063

Browse files
committed
Merge pull request #59 from angularity/bugfix/globbing
fix scalability of globbing
2 parents 0db820e + 38a6905 commit 3521063

File tree

5 files changed

+80
-34
lines changed

5 files changed

+80
-34
lines changed

.travis.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
language: node_js
22

3+
sudo: false
4+
35
node_js:
46
- "0.10"
57

lib/config/lib-globber.js

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
'use strict';
2+
3+
var fs = require('fs'),
4+
path = require('path'),
5+
flatten = require('lodash.flatten'),
6+
minimatch = require('minimatch');
7+
8+
/**
9+
* Define a glob generator that includes files from the local library, where the local library is all subdirectories
10+
* except those given in the <code>exclusions</code>.
11+
* @param {...string|Array} [excludes] Any number of non-library directory names to exclude (glob syntax)
12+
* @returns {function({...string|Array}):function} A function that creates a globber with additions
13+
*/
14+
function libGlobber() {
15+
var excludes = flatten(Array.prototype.slice.call(arguments));
16+
17+
/**
18+
* Create a glob generator that includes items from the exclusion list.
19+
* @param {...string|Array} [additional] Any number of excluded directory names to include (glob syntax)
20+
* @returns {function({...string|Array}):{Array.<string>}} A function that creates a multi-element glob pattern
21+
*/
22+
return function globberWithAdditions() {
23+
var additional = flatten(Array.prototype.slice.call(arguments)),
24+
subdirs = fs.readdirSync(process.cwd())
25+
.filter(testIsDirectory)
26+
.filter(removeExcludesKeepAdditional);
27+
28+
/**
29+
* Create a glob where the given elements are appended to all directores in the library.
30+
* Negative elements appear as-is and are not appended to library directores.
31+
* @param {...string|Array} Any number of glob elements
32+
* @returns {Array.<string>} A multi-element glob pattern
33+
*/
34+
return function doGlob() {
35+
var args = flatten(Array.prototype.slice.call(arguments)),
36+
list = args.reduce(prependSubdirs, []);
37+
return list;
38+
};
39+
40+
function prependSubdirs(reduced, pattern) {
41+
if (pattern.charAt(0) === '!') {
42+
return reduced.concat(pattern);
43+
} else {
44+
return reduced.concat(subdirs.map(function eachSubdir(subdir) {
45+
return subdir + '/' + pattern;
46+
}));
47+
}
48+
}
49+
50+
function testIsDirectory(filename) {
51+
return (filename.charAt(0) !== '.') && fs.statSync(path.resolve(filename)).isDirectory();
52+
}
53+
54+
function removeExcludesKeepAdditional(element) {
55+
return !excludes.some(matchGlobWith(element)) || additional.some(matchGlobWith(element));
56+
}
57+
58+
function matchGlobWith(element) {
59+
return function matchGlob(glob) {
60+
return minimatch(element, glob);
61+
};
62+
}
63+
};
64+
}
65+
66+
module.exports = libGlobber;

lib/config/streams.js

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
'use strict';
22

3-
var gulp = require('gulp'),
4-
path = require('path'),
5-
flatten = require('lodash.flatten'),
6-
reduce = require('lodash.reduce'),
7-
slash = require('gulp-slash'),
8-
bowerDir = require('bower-directory');
3+
var gulp = require('gulp'),
4+
path = require('path'),
5+
slash = require('gulp-slash'),
6+
bowerDir = require('bower-directory');
97

10-
var bowerFiles = require('../inject/bower-files');
8+
var bowerFiles = require('../inject/bower-files'),
9+
libGlobber = require('./lib-globber');
1110

1211
var NODE = 'node_modules',
1312
BOWER = path.relative(process.cwd(), bowerDir.sync()),
@@ -17,29 +16,9 @@ var NODE = 'node_modules',
1716
TEST = 'app-test',
1817
RELEASE_BUNDLE = 'app-release',
1918
RELEASE_VENDOR = 'app-release/vendor',
20-
ROUTES = reduce([ '', BOWER, BUILD ], mapRoutes, { });
19+
ROUTES = ['', BOWER, BUILD].reduce(mapRoutes, {});
2120

22-
/**
23-
* Create a glob generator that only includes files from the local library, unless additional paths are given.
24-
* Additional paths must be of the form <code>NODE</code>, <code>BOWER</code>, <code>APP</code>, or
25-
* <code>GENERATED</code>.
26-
* @param {...string} [additional] Any number of non-library directories to include
27-
* @return {function({Array})} A multi-element glob pattern
28-
*/
29-
function getLocalLibGlob() {
30-
var additional = flatten(Array.prototype.slice.call(arguments));
31-
var excludes = [NODE, BOWER, APP, GENERATED]
32-
.filter(function convertAdditionalToExclude(element) {
33-
return (additional.indexOf(element) < 0);
34-
})
35-
.map(function excludeDirectory(exclude) {
36-
return '!' + exclude + '/**';
37-
});
38-
return function() {
39-
return flatten(Array.prototype.slice.call(arguments))
40-
.concat(excludes); // important - excludes must come after includes
41-
};
42-
}
21+
var getLocalLibGlob = libGlobber(NODE, BOWER, APP, GENERATED);
4322

4423
function mapRoutes(result, path) {
4524
result['/' + slash(path)] = path; // result['/<path>'] = <path>
@@ -51,11 +30,11 @@ function jsApp(opts) {
5130
}
5231

5332
function jsLib(opts) {
54-
return gulp.src(getLocalLibGlob()('**/*.js', '!*.js', '!**/*.spec.js'), opts);
33+
return gulp.src(getLocalLibGlob()('**/*.js', '!**/*.spec.js'), opts);
5534
}
5635

5736
function jsSpec(opts) {
58-
return gulp.src(getLocalLibGlob()('**/*.spec.js', '!*.spec.js'), opts);
37+
return gulp.src(getLocalLibGlob()('**/*.spec.js'), opts);
5938
}
6039

6140
function scssApp(opts) {

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,6 @@
8787
"lodash.defaults": "latest",
8888
"lodash.flatten": "latest",
8989
"lodash.merge": "latest",
90-
"lodash.reduce": "latest",
9190
"lodash.template": "latest",
9291
"mime": "latest",
9392
"minimatch": "latest",

tasks/watch.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ function setUpTaskWatch(context) {
4747

4848
// watch statements
4949
// All JS and HTML sources but not the app HTML
50-
watch(getGlobApp(['**/*.js', '**/*.html', '**/*.json', '!' + streams.APP + '/**/*.html', '!*.*']), {
50+
watch(getGlobApp('**/*.js', '**/*.html', '**/*.json', '!' + streams.APP + '/**/*.html'), {
5151
name : 'JS|HTML',
5252
emitOnGlob: false
5353
}, queue.getHandler('javascript', 'html', 'reload')); // html will be needed in case previous injection failed
5454

5555
// All SASS sources
56-
watch(getGlobApp(['**/*.scss', '!*.scss']), {
56+
watch(getGlobApp('**/*.scss'), {
5757
name : 'CSS',
5858
emitOnGlob: false
5959
}, queue.getHandler('css', 'html', 'reload')); // html will be needed in case previous injection failed

0 commit comments

Comments
 (0)