UNPKG

11.8 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _from = require('babel-runtime/core-js/array/from');
8
9var _from2 = _interopRequireDefault(_from);
10
11var _keys = require('babel-runtime/core-js/object/keys');
12
13var _keys2 = _interopRequireDefault(_keys);
14
15var _getOwnPropertyNames = require('babel-runtime/core-js/object/get-own-property-names');
16
17var _getOwnPropertyNames2 = _interopRequireDefault(_getOwnPropertyNames);
18
19var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
20
21var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
22
23var _assign = require('babel-runtime/core-js/object/assign');
24
25var _assign2 = _interopRequireDefault(_assign);
26
27var _set = require('babel-runtime/core-js/set');
28
29var _set2 = _interopRequireDefault(_set);
30
31var _map = require('babel-runtime/core-js/map');
32
33var _map2 = _interopRequireDefault(_map);
34
35var _graphlib = require('graphlib');
36
37var _events = require('events');
38
39var _events2 = _interopRequireDefault(_events);
40
41var _path = require('path');
42
43var _path2 = _interopRequireDefault(_path);
44
45var _File = require('./File');
46
47var _File2 = _interopRequireDefault(_File);
48
49var _Rule = require('./Rule');
50
51var _Rule2 = _interopRequireDefault(_Rule);
52
53function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
54
55function getLabel(x) {
56 return x instanceof _File2.default ? x.filePath : x.id;
57}
58
59// import _ from 'lodash'
60class State extends _events2.default {
61 // distances: Map<string, number> = new Map()
62 constructor(filePath, schema = []) {
63 super();
64 this.files = new _map2.default();
65 this.rules = new _map2.default();
66 this.options = {};
67 this.defaultOptions = {};
68 this.optionSchema = new _map2.default();
69 this.graphProperties = {};
70 this.ruleClasses = [];
71 this.processes = new _set2.default();
72 this.targets = new _set2.default();
73 this.graph = new _graphlib.Graph();
74 this.optionProxies = {};
75 const resolveFilePath = _path2.default.resolve(filePath);
76 const { dir, base, name, ext } = _path2.default.parse(resolveFilePath);
77 this.filePath = base;
78 this.rootPath = dir;
79 for (const option of schema) {
80 this.optionSchema.set(option.name, option);
81 for (const alias of option.aliases || []) {
82 this.optionSchema.set(alias, option);
83 }
84 if (option.defaultValue) this.defaultOptions[option.name] = option.defaultValue;
85 }
86 this.assignOptions(this.defaultOptions);
87
88 this.env = (0, _assign2.default)({}, process.env, {
89 FILEPATH: base,
90 ROOTDIR: dir,
91 DIR: '.',
92 BASE: base,
93 NAME: name,
94 EXT: ext
95 });
96 if (process.platform === 'win32') {
97 (0, _assign2.default)(this.env, {
98 HOME: process.env.USERPROFILE,
99 PATH: process.env.Path
100 });
101 }
102 }
103
104 getTargetPaths(absolute = false) {
105 var _this = this;
106
107 return (0, _asyncToGenerator3.default)(function* () {
108 const results = [];
109 for (const target of _this.targets.values()) {
110 const file = yield _this.getFile(target);
111 if (file) results.push(absolute ? file.realFilePath : target);
112 }
113 return results;
114 })();
115 }
116
117 getTargetFiles() {
118 var _this2 = this;
119
120 return (0, _asyncToGenerator3.default)(function* () {
121 const results = [];
122 for (const target of _this2.targets.values()) {
123 const file = yield _this2.getFile(target);
124 if (file) results.push(file);
125 }
126 return results;
127 })();
128 }
129
130 removeTarget(filePath) {
131 this.targets.delete(filePath);
132 }
133
134 normalizePath(filePath) {
135 filePath = _path2.default.normalize(filePath);
136
137 if (_path2.default.isAbsolute(filePath)) {
138 const dirPaths = [this.rootPath];
139
140 for (const dir of dirPaths) {
141 const candidateFilePath = _path2.default.relative(dir, filePath);
142 if (!candidateFilePath.startsWith('..')) {
143 return candidateFilePath;
144 }
145 }
146 }
147
148 return filePath;
149 }
150
151 addRule(rule) {
152 var _this3 = this;
153
154 return (0, _asyncToGenerator3.default)(function* () {
155 _this3.rules.set(rule.id, rule);
156 _this3.addNode(rule.id);
157 rule.addActions();
158 })();
159 }
160
161 removeRule(rule) {
162 this.rules.delete(rule.id);
163 this.removeNode(rule.id);
164 }
165
166 addCachedRule(cache) {
167 var _this4 = this;
168
169 return (0, _asyncToGenerator3.default)(function* () {
170 const options = _this4.getJobOptions(cache.jobName);
171 const id = _this4.getRuleId(cache.name, cache.command, cache.phase, cache.jobName, cache.parameters);
172 if (_this4.rules.has(id)) return;
173
174 const RuleClass = _this4.ruleClasses.find(function (ruleClass) {
175 return ruleClass.name === cache.name;
176 });
177 if (RuleClass) {
178 const parameters = [];
179 for (const filePath of cache.parameters) {
180 const parameter = yield _this4.getFile(filePath);
181 if (!parameter) break;
182 parameters.push(parameter);
183 }
184 // $FlowIgnore
185 const rule = new RuleClass(_this4, cache.command, cache.phase, options, parameters);
186 _this4.addNode(rule.id);
187 yield rule.initialize();
188 _this4.rules.set(rule.id, rule);
189 yield rule.getInputs(cache.inputs);
190 const outputs = yield rule.getOutputs(cache.outputs);
191 if (rule.constructor.alwaysEvaluate || outputs.length !== cache.outputs.length) {
192 // At least one of the outputs is missing or the rule should always run.
193 rule.addActions();
194 }
195 for (const input of rule.inputs) {
196 yield rule.addFileActions(input);
197 }
198 }
199 })();
200 }
201
202 getRuleId(name, command, phase, jobName, parameters = []) {
203 const items = [command, phase, jobName || ''].concat(parameters);
204 return `${name}(${items.join(';')})`;
205 }
206
207 getRule(name, command, phase, jobName, parameters = []) {
208 const id = this.getRuleId(name, command, phase, jobName, parameters);
209 return this.rules.get(id);
210 }
211
212 addNode(x) {
213 this.graph.setNode(x);
214 this.graphProperties = {};
215 }
216
217 removeNode(x) {
218 this.graph.removeNode(x);
219 this.graphProperties = {};
220 }
221
222 hasEdge(x, y) {
223 return this.graph.hasEdge(x, y);
224 }
225
226 addEdge(x, y) {
227 this.graph.setEdge(x, y);
228 this.graphProperties = {};
229 }
230
231 removeEdge(x, y) {
232 this.graph.removeEdge(x, y);
233 this.graphProperties = {};
234 }
235
236 getFile(filePath, { timeStamp, hash, value } = {}) {
237 var _this5 = this;
238
239 return (0, _asyncToGenerator3.default)(function* () {
240 filePath = _this5.normalizePath(filePath);
241 let file = _this5.files.get(filePath);
242
243 if (!file) {
244 file = yield _File2.default.create(_path2.default.resolve(_this5.rootPath, filePath), filePath, timeStamp, hash, value);
245 if (!file) {
246 _this5.graph.removeNode(filePath);
247 _this5.emit('fileRemoved', {
248 type: 'fileRemoved',
249 file: filePath
250 });
251 return;
252 }
253 _this5.addNode(filePath);
254 _this5.emit('fileAdded', {
255 type: 'fileAdded',
256 file: filePath,
257 virtual: file.virtual
258 });
259 _this5.files.set(filePath, file);
260 }
261
262 return file;
263 })();
264 }
265
266 deleteFile(file, jobName, unlink = true) {
267 var _this6 = this;
268
269 return (0, _asyncToGenerator3.default)(function* () {
270 const invalidRules = [];
271
272 for (const rule of _this6.rules.values()) {
273 if (rule.jobName === jobName) {
274 if (yield rule.removeFile(file)) {
275 // This file is one of the parameters of the rule so we need to remove
276 // the rule.
277 invalidRules.push(rule);
278 }
279 }
280 }
281
282 for (const rule of invalidRules) {
283 _this6.removeNode(rule.id);
284 }
285
286 if (jobName) file.jobNames.delete(jobName);
287 if (file.jobNames.size === 0) {
288 if (unlink) {
289 yield file.delete();
290 _this6.emit('fileDeleted', {
291 type: 'fileDeleted',
292 file: file.filePath,
293 virtual: file.virtual
294 });
295 }
296 _this6.removeNode(file.filePath);
297 _this6.files.delete(file.filePath);
298 }
299 })();
300 }
301
302 assignSubOptions(to, from) {
303 for (const name in from) {
304 if (from.hasOwnProperty(name)) {
305 const value = from[name];
306 if (typeof value !== 'object' || Array.isArray(value)) {
307 const schema = this.optionSchema.get(name);
308 if (schema) {
309 to[schema.name] = value;
310 } else if (name.startsWith('$')) {
311 // It's an environment variable
312 to[name] = value;
313 } else {
314 // Tell somebody!
315 }
316 } else {
317 if (!(name in to)) to[name] = {};
318 this.assignSubOptions(to[name], value);
319 }
320 }
321 }
322 }
323
324 assignOptions(options) {
325 this.assignSubOptions(this.options, options);
326 }
327
328 resetOptions() {
329 for (const name of (0, _getOwnPropertyNames2.default)(this.options)) {
330 delete this.options[name];
331 }
332
333 this.assignOptions(this.defaultOptions);
334 }
335
336 getJobOptions(jobName = null) {
337 let optionProxy = this.optionProxies[jobName];
338
339 if (!optionProxy) {
340 this.optionProxies[jobName] = optionProxy = new Proxy(this.options, {
341 get: (target, name) => {
342 if (name === 'jobNames') {
343 if ('jobName' in target) return [target.jobName];
344 if ('jobNames' in target) return target.jobNames;
345 if ('jobs' in target) return (0, _keys2.default)(target.jobs);
346 return [null];
347 }
348
349 if (jobName) {
350 if (name === 'jobName') return jobName;
351 if ('jobs' in target) {
352 const jobOptions = target.jobs[jobName];
353 if (jobOptions && name in jobOptions) return jobOptions[name];
354 }
355 }
356
357 const schema = this.optionSchema.get(name);
358
359 if (schema && schema.type === 'boolean') {
360 return !!target[name];
361 }
362
363 return name === 'filePath' ? this.filePath : target[name];
364 },
365 ownKeys: target => {
366 const keys = new _set2.default(['filePath', 'jobNames']);
367
368 if (jobName && 'jobs' in target) {
369 const jobOptions = target.jobs[jobName];
370 if (jobOptions) (0, _keys2.default)(jobOptions).forEach(key => keys.add(key));
371 }
372
373 this.optionSchema.forEach(option => {
374 if (option.type === 'boolean') keys.add(option.name);
375 });
376
377 (0, _keys2.default)(target).forEach(key => keys.add(key));
378
379 keys.delete('jobs');
380
381 return (0, _from2.default)(keys.values());
382 }
383 });
384 }
385
386 return optionProxy;
387 }
388
389 get components() {
390 if (!this.graphProperties.components) {
391 this.graphProperties.components = _graphlib.alg.components(this.graph).map(component => component.map(id => this.rules.get(id)).filter(rule => rule)).filter(component => component.length > 0);
392 }
393
394 return this.graphProperties.components;
395 }
396
397 isGrandparentOf(x, y) {
398 const xLabel = getLabel(x);
399 const yLabel = getLabel(y);
400
401 return this.graph.predecessors(yLabel).some(file => this.graph.predecessors(file).some(r => r === xLabel));
402 }
403
404 getInputRules(file) {
405 const successors = this.graph.successors(file.filePath) || [];
406 // $FlowIgnore
407 return successors.map(id => this.rules.get(id)).filter(rule => rule);
408 }
409
410 getOutputRules(file) {
411 const predecessors = this.graph.predecessors(file.filePath) || [];
412 // $FlowIgnore
413 return predecessors.map(id => this.rules.get(id)).filter(rule => rule);
414 }
415
416 isOutputOf(file, ruleId) {
417 return this.graph.inEdges(file.filePath).some(edge => edge.v.startsWith(ruleId));
418 }
419}
420exports.default = State;
\No newline at end of file