| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- /*
- * Jake JavaScript build tool
- * Copyright 2112 Matthew Eernisse ([email protected])
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
- var fs = require('fs')
- , path = require('path')
- , minimatch = require('minimatch')
- , utils = require('utilities')
- , globSync;
- globSync = function (pat) {
- var dirname = utils.file.basedir(pat)
- , files
- , matches;
- try {
- files = utils.file.readdirR(dirname).map(function(file){
- return file.replace(/\\/g, '/');
- });
- }
- // Bail if path doesn't exist -- assume no files
- catch(e) {
- console.error(e.message);
- }
- if (files) {
- pat = path.normalize(pat);
- // Hack, Minimatch doesn't support backslashes in the pattern
- // https://github.com/isaacs/minimatch/issues/7
- pat = pat.replace(/\\/g, '/');
- matches = minimatch.match(files, pat, {});
- }
- return matches || [];
- };
- // Constants
- // ---------------
- // List of all the builtin Array methods we want to override
- var ARRAY_METHODS = Object.getOwnPropertyNames(Array.prototype)
- // Array methods that return a copy instead of affecting the original
- , SPECIAL_RETURN = {
- 'concat': true
- , 'slice': true
- , 'filter': true
- , 'map': true
- }
- // Default file-patterns we want to ignore
- , DEFAULT_IGNORE_PATTERNS = [
- /(^|[\/\\])CVS([\/\\]|$)/
- , /(^|[\/\\])\.svn([\/\\]|$)/
- , /(^|[\/\\])\.git([\/\\]|$)/
- , /\.bak$/
- , /~$/
- ]
- // Ignore core files
- , DEFAULT_IGNORE_FUNCS = [
- function (name) {
- var isDir = false
- , stats;
- try {
- stats = fs.statSync(name);
- isDir = stats.isDirectory();
- }
- catch(e) {}
- return (/(^|[\/\\])core$/).test(name) && !isDir;
- }
- ];
- var FileList = function () {
- var self = this
- , wrap;
- // List of glob-patterns or specific filenames
- this.pendingAdd = [];
- // Switched to false after lazy-eval of files
- this.pending = true;
- // Used to calculate exclusions from the list of files
- this.excludes = {
- pats: DEFAULT_IGNORE_PATTERNS.slice()
- , funcs: DEFAULT_IGNORE_FUNCS.slice()
- , regex: null
- };
- this.items = [];
- // Wrap the array methods with the delegates
- wrap = function (prop) {
- var arr;
- self[prop] = function () {
- if (self.pending) {
- self.resolve();
- }
- if (typeof self.items[prop] == 'function') {
- // Special method that return a copy
- if (SPECIAL_RETURN[prop]) {
- arr = self.items[prop].apply(self.items, arguments);
- return FileList.clone(self, arr);
- }
- else {
- return self.items[prop].apply(self.items, arguments);
- }
- }
- else {
- return self.items[prop];
- }
- };
- };
- for (var i = 0, ii = ARRAY_METHODS.length; i < ii; i++) {
- wrap(ARRAY_METHODS[i]);
- }
- // Include whatever files got passed to the constructor
- this.include.apply(this, arguments);
- // Fix constructor linkage
- this.constructor = FileList;
- };
- FileList.prototype = new (function () {
- var globPattern = /[*?\[\{]/;
- var _addMatching = function (pat) {
- var matches = globSync(pat);
- this.items = this.items.concat(matches);
- }
- , _resolveAdd = function (name) {
- if (globPattern.test(name)) {
- _addMatching.call(this, name);
- }
- else {
- this.push(name);
- }
- }
- , _calculateExcludeRe = function () {
- var pats = this.excludes.pats
- , pat
- , excl = []
- , matches = [];
- for (var i = 0, ii = pats.length; i < ii; i++) {
- pat = pats[i];
- if (typeof pat == 'string') {
- // Glob, look up files
- if (/[*?]/.test(pat)) {
- matches = globSync(pat);
- matches = matches.map(function (m) {
- return utils.string.escapeRegExpChars(m);
- });
- excl = excl.concat(matches);
- }
- // String for regex
- else {
- excl.push(utils.string.escapeRegExpChars(pat));
- }
- }
- // Regex, grab the string-representation
- else if (pat instanceof RegExp) {
- excl.push(pat.toString().replace(/^\/|\/$/g, ''));
- }
- }
- if (excl.length) {
- this.excludes.regex = new RegExp('(' + excl.join(')|(') + ')');
- }
- else {
- this.excludes.regex = /^$/;
- }
- }
- , _resolveExclude = function () {
- var self = this;
- _calculateExcludeRe.call(this);
- // No `reject` method, so use reverse-filter
- this.items = this.items.filter(function (name) {
- return !self.shouldExclude(name);
- });
- };
- /**
- * Includes file-patterns in the FileList. Should be called with one or more
- * pattern for finding file to include in the list. Arguments should be strings
- * for either a glob-pattern or a specific file-name, or an array of them
- */
- this.include = function (items) {
- if (items) {
- this.pendingAdd = this.pendingAdd.concat(items).filter(function (item) {
- return !!item;
- });
- }
- return this;
- };
- /**
- * Indicates whether a particular file would be filtered out by the current
- * exclusion rules for this FileList.
- * @param {String} name The filename to check
- * @return {Boolean} Whether or not the file should be excluded
- */
- this.shouldExclude = function (name) {
- if (!this.excludes.regex) {
- _calculateExcludeRe.call(this);
- }
- var excl = this.excludes;
- return excl.regex.test(name) || excl.funcs.some(function (f) {
- return !!f(name);
- });
- };
- /**
- * Excludes file-patterns from the FileList. Should be called with one or more
- * pattern for finding file to include in the list. Arguments can be:
- * 1. Strings for either a glob-pattern or a specific file-name
- * 2. Regular expression literals
- * 3. Functions to be run on the filename that return a true/false
- */
- this.exclude = function () {
- var args = Array.isArray(arguments[0]) ? arguments[0] : arguments
- , arg;
- for (var i = 0, ii = args.length; i < ii; i++) {
- arg = args[i];
- if (typeof arg == 'function' && !(arg instanceof RegExp)) {
- this.excludes.funcs.push(arg);
- }
- else {
- this.excludes.pats.push(arg);
- }
- }
- if (!this.pending) {
- _resolveExclude.call(this);
- }
- return this;
- };
- /**
- * Populates the FileList from the include/exclude rules with a list of
- * actual files
- */
- this.resolve = function () {
- var name
- , uniqueFunc = function (p, c) {
- if (p.indexOf(c) < 0) {
- p.push(c);
- }
- return p;
- };
- if (this.pending) {
- this.pending = false;
- while ((name = this.pendingAdd.shift())) {
- _resolveAdd.call(this, name);
- }
- // Reduce to a unique list
- this.items = this.items.reduce(uniqueFunc, []);
- // Remove exclusions
- _resolveExclude.call(this);
- }
- return this;
- };
- /**
- * Convert to a plain-jane array
- */
- this.toArray = function () {
- // Call slice to ensure lazy-resolution before slicing items
- var ret = this.slice().items.slice();
- return ret;
- };
- /**
- * Clear any pending items -- only useful before
- * calling `resolve`
- */
- this.clearInclusions = function () {
- this.pendingAdd = [];
- return this;
- };
- /**
- * Clear any current exclusion rules
- */
- this.clearExclusions = function () {
- this.excludes = {
- pats: []
- , funcs: []
- , regex: null
- };
- return this;
- };
- })();
- // Static method, used to create copy returned by special
- // array methods
- FileList.clone = function (list, items) {
- var clone = new FileList();
- if (items) {
- clone.items = items;
- }
- clone.pendingAdd = list.pendingAdd;
- clone.pending = list.pending;
- for (var p in list.excludes) {
- clone.excludes[p] = list.excludes[p];
- }
- return clone;
- };
- exports.FileList = FileList;
|