-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.js
198 lines (160 loc) · 5.4 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
var path = require('path');
var ConfigFile = require('requirejs-config-file').ConfigFile;
var glob = require('glob-all');
var debug = require('debug')('dependents');
var extend = require('extend');
var computeDependents = require('./lib/computeDependents');
var util = require('./lib/util');
var WorkerManager = require('./lib/WorkerManager');
/**
* Computes the dependents for the given file across a directory or list of filenames
*
* @param {Object} options
* @param {String} options.filename - The file whose dependents to compute
* @param {String|Array} options.directory - Directory name or list of filenames to process
* @param {Number} [options.parallelThreshold=500] - The number of files that triggers multi-core processing
* @param {String} [options.config] - Path to the shim config
* @param {String[]} [options.exclusions] - List of files and directories to exclude
* @param {String[]} [options.files] - Allows workers to process predetermined sets of files
*
*/
module.exports = function(options, cb) {
if (!cb) { throw new Error('expected success callback'); }
if (!options || !options.filename) {
cb(new Error('expected a filename'));
}
if (!options.directory) { cb(new Error('expected directory name')); }
var directory = util.stripTrailingSlash(options.directory);
var filename = path.resolve(options.filename);
var exclusions = options.exclusions || [];
var config = options.config;
var webpackConfig = options.webpackConfig;
debug('filename: ' + filename);
debug('directory: ' + directory);
debug('config: ' + config);
debug('webpackConfig: ' + webpackConfig);
debug('exclusions: ', exclusions);
if (typeof exclusions === 'string') {
exclusions = exclusions.split(',');
}
var _excludes = module.exports.DEFAULT_EXCLUDE_DIR.concat(exclusions);
debug('exclusions w/ defaults: ', _excludes);
exclusions = util.processExcludes(_excludes, directory);
debug('processed exclusions: ', exclusions);
if (config && typeof config === 'string') {
debug('converting the config path to an object');
config = module.exports._readConfig(options.config);
}
// Look-up-table whose keys are filenames of JS files in the directory
// the value for each key is an object of (filename -> dummy value)
// files that depend on the key
var dependents = {};
var files = options.files;
if (!files) {
files = getFilesToProcess({
filename: filename,
directory: directory,
exclusions: exclusions
});
debug('Grabbed ' + files.length + ' files');
}
var configuration = {
directory: directory,
config: config,
configPath: typeof options.config === 'string' ? options.config : options.configPath,
webpackConfig: webpackConfig
};
debug('configuration: \n', configuration);
if (module.exports._shouldParallelize(files, Number(options.parallelThreshold))) {
configuration.files = files;
var workerManager = new WorkerManager(configuration);
workerManager.computeAllDependents()
.then(function(allDependents) {
cb(null, Object.keys(allDependents[filename]));
})
.catch(cb);
return;
}
files.forEach(function(filename) {
configuration.filename = filename;
var results = computeDependents(configuration);
debug('computed dependents map', results);
extend(true, dependents, results);
});
debug('final dependents map', dependents);
debug('dependents for ' + filename + ': ', dependents[filename]);
// Default to empty object if the given file isn't in the map
cb(null, Object.keys(dependents[filename] || {}));
};
/**
* Exposed for testing
*
* @private
* @param {String} configPath
* @return {Object}
*/
module.exports._readConfig = function(configPath) {
return new ConfigFile(configPath).read();
};
/**
* Exposed for testing
*
* @param {String[]} files
* @return {Boolean}
*/
module.exports._shouldParallelize = function(files, minimumNumberOfFiles) {
minimumNumberOfFiles = typeof minimumNumberOfFiles === 'undefined' ? 500 : minimumNumberOfFiles;
return files.length >= minimumNumberOfFiles;
};
/**
* Set of directories to ignore by default
*
* @type {Array}
*/
module.exports.DEFAULT_EXCLUDE_DIR = [
'node_modules',
'bower_components',
'vendor'
];
/**
* @param {Object} options
* @return {String[]}
*/
function getFilesToProcess(options) {
var filename = options.filename;
var exclusions = options.exclusions;
var directory = options.directory;
var exts = getExtensionsToProcess(filename);
var extensions = exts.length > 1 ?
'+(' + exts.join('|') + ')' :
exts[0];
var globbers = [
directory + '/**/*' + extensions
];
globbers = globbers.concat(exclusions.directories.map(function(d) {
return '!' + directory + '/' + d + '/**/*';
})
.concat(exclusions.files.map(function(f) {
return '!' + directory + '/**/' + f;
})));
debug('globbers: ' + globbers.join('\n'));
return glob.sync(globbers);
}
/**
* Get a list of possible file extensions associated with the given file
*
* @param {String} filename
* @return {String[]}
*/
function getExtensionsToProcess(filename) {
var exts = [''];
if (util.isSassFile(filename)) {
exts = ['.scss', '.sass'];
} else if (util.isStylusFile(filename)) {
exts = ['.styl'];
// Which extensions can possibly include the given file
} else {
exts = ['.js', '.styl', '.sass', '.scss'];
}
return exts;
}