Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80657 views
1
/**
2
* Copyright 2013 Facebook, Inc.
3
*
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
7
*
8
* http://www.apache.org/licenses/LICENSE-2.0
9
*
10
* Unless required by applicable law or agreed to in writing, software
11
* distributed under the License is distributed on an "AS IS" BASIS,
12
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
* See the License for the specific language governing permissions and
14
* limitations under the License.
15
*/
16
var path = require('path');
17
var MessageList = require('./MessageList');
18
19
function AnalyzeChangedTask(loaders, configurationTrie, options) {
20
this.loaders = loaders;
21
this.configurationTrie = configurationTrie;
22
this.maxOpenFiles = options && options.maxOpenFiles || 200;
23
this.maxProcesses = options && options.maxProcesses || 4;
24
}
25
26
AnalyzeChangedTask.fromObject = function(object) {
27
var ResourceLoader = require('./loader/ResourceLoader');
28
var loaders = object.loaders.map(ResourceLoader.fromObject, this);
29
var ConfigurationTrie = require('./ConfigurationTrie');
30
var trie = new ConfigurationTrie.fromObject(object.trie);
31
return new AnalyzeChangedTask(loaders, trie, {
32
maxOpenFiles: object.maxOpenFiles
33
});
34
};
35
36
AnalyzeChangedTask.prototype.toObject = function() {
37
return {
38
loaders: this.loaders.map(function(l) {
39
return l.toObject();
40
}),
41
trie: this.configurationTrie.toObject(),
42
maxOpenFiles: this.maxOpenFiles
43
};
44
};
45
46
AnalyzeChangedTask.prototype.runInForks = function(n, paths, callback) {
47
var buckets = [];
48
var waiting = n;
49
for (var i = 0; i < n; i++) {
50
buckets[i] = [];
51
}
52
paths.forEach(function(p, i) {
53
buckets[i % n].push(p);
54
});
55
56
var skipped = [];
57
var messages = MessageList.create();
58
var resources = [];
59
var complete = function() {
60
if (--waiting === 0) {
61
callback(messages, resources, skipped);
62
}
63
};
64
65
var typeMap = {};
66
this.loaders.forEach(function(loader) {
67
loader.getResourceTypes().forEach(function(type) {
68
typeMap[type.prototype.type] = type;
69
});
70
});
71
72
var cp = require('child_process');
73
buckets.forEach(function() {
74
var child = cp.fork(__dirname + '/analyze-changed.js', [], {
75
// Passing --debug to child processes interferes with the --debug socket
76
// of the parent process.
77
execArgv: process.execArgv.filter(function(arg) {
78
return arg.indexOf('--debug') === -1;
79
})
80
});
81
82
83
child.on('message', function(m) {
84
messages.mergeAndRecycle(MessageList.fromObject(m.messages));
85
m.resources.forEach(function(obj) {
86
var type = typeMap[obj.type];
87
resources.push(type.fromObject(obj));
88
});
89
skipped = skipped.concat(m.skipped);
90
if (paths.length === 0) {
91
child.send({ exit: 1 });
92
complete();
93
} else {
94
var chunkSize = Math.min(
95
this.maxOpenFiles,
96
Math.ceil(paths.length / n));
97
child.send({ paths: paths.splice(0, chunkSize) });
98
}
99
}.bind(this));
100
101
child.send({
102
task: this.toObject(),
103
paths: paths.splice(0, this.maxOpenFiles)
104
});
105
}, this);
106
};
107
108
AnalyzeChangedTask.prototype.runOptimaly = function(paths, callback) {
109
var n = Math.min(
110
this.maxProcesses,
111
Math.floor(paths.length / this.maxOpenFiles));
112
if (n > 1) {
113
this.runInForks(n, paths, callback);
114
} else {
115
this.run(paths, callback);
116
}
117
};
118
119
AnalyzeChangedTask.prototype.run = function(paths, callback) {
120
var trie = this.configurationTrie;
121
var loaders = this.loaders;
122
var maxOpenFiles = this.maxOpenFiles;
123
124
var messages = MessageList.create();
125
var waiting = paths.length;
126
var active = 0;
127
var next;
128
var result = [];
129
var skipped = [];
130
131
function resourceLoaded(m, resource) {
132
messages.mergeAndRecycle(m);
133
result.push(resource);
134
waiting--;
135
active--;
136
next();
137
}
138
139
next = function() {
140
var node_path = require('path');
141
while (active < maxOpenFiles && paths.length) {
142
var path = paths.shift();
143
var loader = null;
144
145
for (var i = 0; i < loaders.length; i++) {
146
if (loaders[i].matchPath(path)) {
147
loader = loaders[i];
148
break;
149
}
150
}
151
152
if (loader) {
153
active++;
154
var configuration = trie.findConfiguration(node_path.normalize(path));
155
loader.loadFromPath(path, configuration, resourceLoaded);
156
} else {
157
// if we reached this point the resource was not analyzed because of the
158
// missing type
159
skipped.push(path);
160
waiting--;
161
}
162
}
163
if (waiting === 0 && paths.length === 0) {
164
callback(messages, result, skipped);
165
}
166
};
167
168
next();
169
};
170
171
module.exports = AnalyzeChangedTask;
172
173