1 | var glob = require('glob');
|
2 | var path = require('path');
|
3 | var fs = require('../util/fs');
|
4 | var Q = require('q');
|
5 | var mout = require('mout');
|
6 | var rimraf = require('../util/rimraf');
|
7 | var endpointParser = require('bower-endpoint-parser');
|
8 | var Logger = require('bower-logger');
|
9 | var md5 = require('md5-hex');
|
10 | var Manager = require('./Manager');
|
11 | var semver = require('../util/semver');
|
12 | var createError = require('../util/createError');
|
13 | var readJson = require('../util/readJson');
|
14 | var validLink = require('../util/validLink');
|
15 | var scripts = require('./scripts');
|
16 | var relativeToBaseDir = require('../util/relativeToBaseDir');
|
17 |
|
18 | function Project(config, logger) {
|
19 | this._config = config;
|
20 | this._logger = logger || new Logger();
|
21 | this._manager = new Manager(this._config, this._logger);
|
22 |
|
23 | this._options = {};
|
24 | }
|
25 |
|
26 |
|
27 |
|
28 | Project.prototype.install = function(decEndpoints, options, config) {
|
29 | var that = this;
|
30 | var targets = [];
|
31 | var resolved = {};
|
32 | var incompatibles = [];
|
33 |
|
34 |
|
35 | if (this._working) {
|
36 | return Q.reject(createError('Already working', 'EWORKING'));
|
37 | }
|
38 |
|
39 | this._options = options || {};
|
40 | this._config = config || {};
|
41 | this._working = true;
|
42 |
|
43 |
|
44 | return this._analyse()
|
45 | .spread(function(json, tree) {
|
46 |
|
47 |
|
48 | if (!that._jsonFile && decEndpoints.length === 0) {
|
49 | throw createError('No bower.json present', 'ENOENT');
|
50 | }
|
51 |
|
52 |
|
53 | that.walkTree(
|
54 | tree,
|
55 | function(node, name) {
|
56 | if (node.incompatible) {
|
57 | incompatibles.push(node);
|
58 | } else if (
|
59 | node.missing ||
|
60 | node.different ||
|
61 | that._config.force
|
62 | ) {
|
63 | targets.push(node);
|
64 | } else {
|
65 | resolved[name] = node;
|
66 | }
|
67 | },
|
68 | true
|
69 | );
|
70 |
|
71 |
|
72 | decEndpoints = decEndpoints || [];
|
73 | decEndpoints.forEach(function(decEndpoint) {
|
74 |
|
75 |
|
76 |
|
77 |
|
78 | decEndpoint.newly = true;
|
79 | targets.push(decEndpoint);
|
80 | });
|
81 |
|
82 |
|
83 | return that._bootstrap(targets, resolved, incompatibles);
|
84 | })
|
85 | .then(function() {
|
86 | return that._manager.preinstall(that._json);
|
87 | })
|
88 | .then(function() {
|
89 | return that._manager.install(that._json);
|
90 | })
|
91 | .then(function(installed) {
|
92 |
|
93 | if (
|
94 | that._options.save ||
|
95 | that._options.saveDev ||
|
96 | that._options.saveExact ||
|
97 | that._config.save ||
|
98 | that._config.saveExact
|
99 | ) {
|
100 |
|
101 | decEndpoints.forEach(function(decEndpoint) {
|
102 | var jsonEndpoint;
|
103 |
|
104 | jsonEndpoint = endpointParser.decomposed2json(decEndpoint);
|
105 |
|
106 | if (that._options.saveExact || that._config.saveExact) {
|
107 | if (decEndpoint.name !== decEndpoint.source) {
|
108 | jsonEndpoint[decEndpoint.name] =
|
109 | decEndpoint.source +
|
110 | '#' +
|
111 | decEndpoint.pkgMeta.version;
|
112 | } else {
|
113 | jsonEndpoint[decEndpoint.name] =
|
114 | decEndpoint.pkgMeta.version;
|
115 | }
|
116 | }
|
117 |
|
118 | if (that._options.saveDev) {
|
119 | that._json.devDependencies = mout.object.mixIn(
|
120 | that._json.devDependencies || {},
|
121 | jsonEndpoint
|
122 | );
|
123 | } else {
|
124 | that._json.dependencies = mout.object.mixIn(
|
125 | that._json.dependencies || {},
|
126 | jsonEndpoint
|
127 | );
|
128 | }
|
129 | });
|
130 | }
|
131 |
|
132 |
|
133 | return that.saveJson().then(function() {
|
134 | return that._manager.postinstall(that._json).then(function() {
|
135 | return installed;
|
136 | });
|
137 | });
|
138 | })
|
139 | .fin(function() {
|
140 | that._installed = null;
|
141 | that._working = false;
|
142 | });
|
143 | };
|
144 |
|
145 | Project.prototype.update = function(names, options) {
|
146 | var that = this;
|
147 | var targets = [];
|
148 | var resolved = {};
|
149 | var incompatibles = [];
|
150 |
|
151 |
|
152 | if (this._working) {
|
153 | return Q.reject(createError('Already working', 'EWORKING'));
|
154 | }
|
155 |
|
156 | this._options = options || {};
|
157 | this._working = true;
|
158 |
|
159 |
|
160 | return this._analyse()
|
161 | .spread(function(json, tree, flattened) {
|
162 |
|
163 | if (!names) {
|
164 |
|
165 | that.walkTree(
|
166 | tree,
|
167 | function(node) {
|
168 |
|
169 |
|
170 | if (node.linked) {
|
171 | targets.push.apply(
|
172 | targets,
|
173 | mout.object.values(node.dependencies)
|
174 | );
|
175 | } else {
|
176 | targets.push(node);
|
177 | }
|
178 |
|
179 | return false;
|
180 | },
|
181 | true
|
182 | );
|
183 |
|
184 | } else {
|
185 |
|
186 |
|
187 | names.forEach(function(name) {
|
188 | if (!flattened[name]) {
|
189 | throw createError(
|
190 | 'Package ' + name + ' is not installed',
|
191 | 'ENOTINS',
|
192 | {
|
193 | name: name
|
194 | }
|
195 | );
|
196 | }
|
197 | });
|
198 |
|
199 |
|
200 | that.walkTree(
|
201 | tree,
|
202 | function(node, name) {
|
203 | if (names.indexOf(name) !== -1) {
|
204 |
|
205 |
|
206 | if (node.linked) {
|
207 | targets.push.apply(
|
208 | targets,
|
209 | mout.object.values(node.dependencies)
|
210 | );
|
211 | } else {
|
212 | targets.push(node);
|
213 | }
|
214 |
|
215 | return false;
|
216 | }
|
217 | },
|
218 | true
|
219 | );
|
220 |
|
221 |
|
222 | that.walkTree(
|
223 | tree,
|
224 | function(node, name) {
|
225 | if (node.missing || node.different) {
|
226 | targets.push(node);
|
227 | } else if (node.incompatible) {
|
228 | incompatibles.push(node);
|
229 | } else {
|
230 | resolved[name] = node;
|
231 | }
|
232 | },
|
233 | true
|
234 | );
|
235 | }
|
236 |
|
237 |
|
238 | return that
|
239 | ._bootstrap(targets, resolved, incompatibles)
|
240 | .then(function() {
|
241 | return that._manager.preinstall(that._json);
|
242 | })
|
243 | .then(function() {
|
244 | return that._manager.install(that._json);
|
245 | })
|
246 | .then(function(installed) {
|
247 |
|
248 | return that.saveJson().then(function() {
|
249 | return that._manager
|
250 | .postinstall(that._json)
|
251 | .then(function() {
|
252 | return installed;
|
253 | });
|
254 | });
|
255 | });
|
256 | })
|
257 | .fin(function() {
|
258 | that._installed = null;
|
259 | that._working = false;
|
260 | });
|
261 | };
|
262 |
|
263 | function resolveUrlNames(names, flattened) {
|
264 | for (var i = 0; i < names.length; i++)
|
265 | if (!flattened[names[i]]) {
|
266 | var url = names[i].trim().replace(/\/$/, '');
|
267 | var packName;
|
268 | for (packName in flattened)
|
269 | if (!!flattened[packName].source)
|
270 | if (
|
271 | url ==
|
272 | flattened[packName].source.trim().replace(/\/$/, '')
|
273 | )
|
274 | names[i] = packName;
|
275 | }
|
276 | }
|
277 |
|
278 | Project.prototype.uninstall = function(names, options) {
|
279 | var that = this;
|
280 | var packages = {};
|
281 |
|
282 |
|
283 | if (this._working) {
|
284 | return Q.reject(createError('Already working', 'EWORKING'));
|
285 | }
|
286 |
|
287 | this._options = options || {};
|
288 | this._working = true;
|
289 |
|
290 |
|
291 | return (
|
292 | this._analyse()
|
293 |
|
294 | .spread(function(json, tree, flattened) {
|
295 | var promise = Q.resolve();
|
296 | resolveUrlNames(names, flattened);
|
297 |
|
298 | names.forEach(function(name) {
|
299 | var decEndpoint = flattened[name];
|
300 |
|
301 |
|
302 | if (!decEndpoint || decEndpoint.missing) {
|
303 | packages[name] = null;
|
304 | return;
|
305 | }
|
306 |
|
307 | promise = promise.then(function() {
|
308 | var message;
|
309 | var data;
|
310 | var dependantsNames;
|
311 | var dependants = [];
|
312 |
|
313 |
|
314 | that.walkTree(
|
315 | tree,
|
316 | function(node, nodeName) {
|
317 | if (name === nodeName) {
|
318 | dependants.push.apply(
|
319 | dependants,
|
320 | mout.object.values(node.dependants)
|
321 | );
|
322 | }
|
323 | },
|
324 | true
|
325 | );
|
326 |
|
327 |
|
328 | dependants = mout.array.unique(dependants);
|
329 |
|
330 |
|
331 |
|
332 | dependants = dependants.filter(function(dependant) {
|
333 | return (
|
334 | !dependant.root &&
|
335 | names.indexOf(dependant.name) === -1
|
336 | );
|
337 | });
|
338 |
|
339 |
|
340 |
|
341 | if (!dependants.length || that._config.force) {
|
342 | packages[name] = decEndpoint.canonicalDir;
|
343 | return;
|
344 | }
|
345 |
|
346 |
|
347 |
|
348 |
|
349 | dependantsNames = dependants.map(function(dep) {
|
350 | return dep.name;
|
351 | });
|
352 | dependantsNames.sort(function(name1, name2) {
|
353 | return name1.localeCompare(name2);
|
354 | });
|
355 | dependantsNames = mout.array.unique(dependantsNames);
|
356 | dependants = dependants.map(function(dependant) {
|
357 | return that._manager.toData(dependant);
|
358 | });
|
359 | message =
|
360 | dependantsNames.join(', ') +
|
361 | ' depends on ' +
|
362 | decEndpoint.name;
|
363 | data = {
|
364 | name: decEndpoint.name,
|
365 | dependants: dependants
|
366 | };
|
367 |
|
368 |
|
369 | if (!that._config.interactive) {
|
370 | throw createError(message, 'ECONFLICT', {
|
371 | data: data
|
372 | });
|
373 | }
|
374 |
|
375 | that._logger.conflict('mutual', message, data);
|
376 |
|
377 |
|
378 | return Q.nfcall(
|
379 | that._logger.prompt.bind(that._logger),
|
380 | {
|
381 | type: 'confirm',
|
382 | message: 'Continue anyway?',
|
383 | default: true
|
384 | }
|
385 | ).then(function(confirmed) {
|
386 |
|
387 |
|
388 | if (!confirmed) {
|
389 | mout.array.remove(names, name);
|
390 | } else {
|
391 | packages[name] = decEndpoint.canonicalDir;
|
392 | }
|
393 | });
|
394 | });
|
395 | });
|
396 |
|
397 | return promise;
|
398 | })
|
399 |
|
400 | .then(function() {
|
401 | return that._removePackages(packages);
|
402 | })
|
403 | .fin(function() {
|
404 | that._installed = null;
|
405 | that._working = false;
|
406 | })
|
407 | );
|
408 | };
|
409 |
|
410 | Project.prototype.getTree = function(options) {
|
411 | this._options = options || {};
|
412 |
|
413 | return this._analyse().spread(
|
414 | function(json, tree, flattened) {
|
415 | var extraneous = [];
|
416 | var additionalKeys = [
|
417 | 'missing',
|
418 | 'extraneous',
|
419 | 'different',
|
420 | 'linked'
|
421 | ];
|
422 |
|
423 |
|
424 | tree = this._manager.toData(tree, additionalKeys);
|
425 |
|
426 |
|
427 | this.walkTree(
|
428 | tree,
|
429 | function(node) {
|
430 | var version;
|
431 | var target = node.endpoint.target;
|
432 |
|
433 | if (node.pkgMeta && semver.validRange(target)) {
|
434 | version = node.pkgMeta.version;
|
435 |
|
436 |
|
437 | if (!version && target === '*') {
|
438 | return;
|
439 | }
|
440 |
|
441 | if (!version || !semver.satisfies(version, target)) {
|
442 | node.incompatible = true;
|
443 | }
|
444 | }
|
445 | },
|
446 | true
|
447 | );
|
448 |
|
449 |
|
450 | mout.object.forOwn(
|
451 | flattened,
|
452 | function(pkg) {
|
453 | if (pkg.extraneous) {
|
454 | extraneous.push(
|
455 | this._manager.toData(pkg, additionalKeys)
|
456 | );
|
457 | }
|
458 | },
|
459 | this
|
460 | );
|
461 |
|
462 |
|
463 | flattened = mout.object.map(
|
464 | flattened,
|
465 | function(node) {
|
466 | return this._manager.toData(node, additionalKeys);
|
467 | },
|
468 | this
|
469 | );
|
470 |
|
471 | return [tree, flattened, extraneous];
|
472 | }.bind(this)
|
473 | );
|
474 | };
|
475 |
|
476 | Project.prototype.walkTree = function(node, fn, onlyOnce) {
|
477 | var result;
|
478 | var dependencies;
|
479 | var queue = mout.object.values(node.dependencies);
|
480 |
|
481 | if (onlyOnce === true) {
|
482 | onlyOnce = [];
|
483 | }
|
484 |
|
485 | while (queue.length) {
|
486 | node = queue.shift();
|
487 | result = fn(node, node.endpoint ? node.endpoint.name : node.name);
|
488 |
|
489 |
|
490 | if (result === false) {
|
491 | continue;
|
492 | }
|
493 |
|
494 |
|
495 | dependencies = mout.object.values(node.dependencies);
|
496 |
|
497 |
|
498 | if (onlyOnce) {
|
499 | dependencies = dependencies.filter(function(dependency) {
|
500 | return !mout.array.find(onlyOnce, function(stacked) {
|
501 | if (dependency.endpoint) {
|
502 | return mout.object.equals(
|
503 | dependency.endpoint,
|
504 | stacked.endpoint
|
505 | );
|
506 | }
|
507 |
|
508 | return (
|
509 | dependency.name === stacked.name &&
|
510 | dependency.source === stacked.source &&
|
511 | dependency.target === stacked.target
|
512 | );
|
513 | });
|
514 | });
|
515 |
|
516 | onlyOnce.push.apply(onlyOnce, dependencies);
|
517 | }
|
518 |
|
519 | queue.unshift.apply(queue, dependencies);
|
520 | }
|
521 | };
|
522 |
|
523 | Project.prototype.saveJson = function(forceCreate) {
|
524 | var file;
|
525 | var jsonStr = JSON.stringify(this._json, null, ' ') + '\n';
|
526 | var jsonHash = md5(jsonStr);
|
527 |
|
528 |
|
529 | if (jsonHash === this._jsonHash) {
|
530 | return Q.resolve();
|
531 | }
|
532 |
|
533 |
|
534 |
|
535 | if (!this._jsonFile && !forceCreate) {
|
536 | this._logger.warn(
|
537 | 'no-json',
|
538 | 'No bower.json file to save to, use bower init to create one'
|
539 | );
|
540 | return Q.resolve();
|
541 | }
|
542 |
|
543 | file = this._jsonFile || path.join(this._config.cwd, 'bower.json');
|
544 | return Q.nfcall(fs.writeFile, file, jsonStr).then(
|
545 | function() {
|
546 | this._jsonHash = jsonHash;
|
547 | this._jsonFile = file;
|
548 | return this._json;
|
549 | }.bind(this)
|
550 | );
|
551 | };
|
552 |
|
553 | Project.prototype.hasJson = function() {
|
554 | return this._readJson().then(
|
555 | function(json) {
|
556 | return json ? this._jsonFile : false;
|
557 | }.bind(this)
|
558 | );
|
559 | };
|
560 |
|
561 | Project.prototype.getJson = function() {
|
562 | return this._readJson();
|
563 | };
|
564 |
|
565 | Project.prototype.getManager = function() {
|
566 | return this._manager;
|
567 | };
|
568 |
|
569 | Project.prototype.getPackageRepository = function() {
|
570 | return this._manager.getPackageRepository();
|
571 | };
|
572 |
|
573 |
|
574 |
|
575 | Project.prototype._analyse = function() {
|
576 | return Q.all([
|
577 | this._readJson(),
|
578 | this._readInstalled(),
|
579 | this._readLinks()
|
580 | ]).spread(
|
581 | function(json, installed, links) {
|
582 | var root;
|
583 | var jsonCopy = mout.lang.deepClone(json);
|
584 |
|
585 | root = {
|
586 | name: json.name,
|
587 | source: this._config.cwd,
|
588 | target: json.version || '*',
|
589 | pkgMeta: jsonCopy,
|
590 | canonicalDir: this._config.cwd,
|
591 | root: true
|
592 | };
|
593 |
|
594 | mout.object.mixIn(installed, links);
|
595 |
|
596 |
|
597 |
|
598 | jsonCopy.dependencies = jsonCopy.dependencies || {};
|
599 | jsonCopy.devDependencies = jsonCopy.devDependencies || {};
|
600 | mout.object.forOwn(installed, function(decEndpoint, key) {
|
601 | var pkgMeta = decEndpoint.pkgMeta;
|
602 | var isSaved =
|
603 | jsonCopy.dependencies[key] || jsonCopy.devDependencies[key];
|
604 |
|
605 |
|
606 |
|
607 | if (!isSaved && pkgMeta && pkgMeta._direct) {
|
608 | decEndpoint.extraneous = true;
|
609 |
|
610 | if (decEndpoint.linked) {
|
611 | jsonCopy.dependencies[key] = pkgMeta.version || '*';
|
612 | } else {
|
613 | jsonCopy.dependencies[key] =
|
614 | (pkgMeta._originalSource || pkgMeta._source) +
|
615 | '#' +
|
616 | pkgMeta._target;
|
617 | }
|
618 | }
|
619 | });
|
620 |
|
621 |
|
622 |
|
623 | this._restoreNode(root, installed, 'dependencies');
|
624 |
|
625 | if (!this._options.production) {
|
626 | this._restoreNode(root, installed, 'devDependencies');
|
627 | }
|
628 |
|
629 |
|
630 | mout.object.forOwn(
|
631 | installed,
|
632 | function(decEndpoint, name) {
|
633 | if (!decEndpoint.dependants) {
|
634 | decEndpoint.extraneous = true;
|
635 | this._restoreNode(
|
636 | decEndpoint,
|
637 | installed,
|
638 | 'dependencies'
|
639 | );
|
640 |
|
641 | root.dependencies[name] = decEndpoint;
|
642 | }
|
643 | },
|
644 | this
|
645 | );
|
646 |
|
647 |
|
648 | delete installed[json.name];
|
649 |
|
650 | return [json, root, installed];
|
651 | }.bind(this)
|
652 | );
|
653 | };
|
654 |
|
655 | Project.prototype._bootstrap = function(targets, resolved, incompatibles) {
|
656 | var installed = mout.object.map(this._installed, function(decEndpoint) {
|
657 | return decEndpoint.pkgMeta;
|
658 | });
|
659 |
|
660 | this._json.resolutions = this._json.resolutions || {};
|
661 |
|
662 |
|
663 | return this._manager
|
664 | .configure({
|
665 | targets: targets,
|
666 | resolved: resolved,
|
667 | incompatibles: incompatibles,
|
668 | resolutions: this._json.resolutions,
|
669 | installed: installed,
|
670 | forceLatest: this._options.forceLatest
|
671 | })
|
672 | .resolve()
|
673 | .then(
|
674 | function() {
|
675 |
|
676 | if (!mout.object.size(this._json.resolutions)) {
|
677 | delete this._json.resolutions;
|
678 | }
|
679 | }.bind(this)
|
680 | );
|
681 | };
|
682 |
|
683 | Project.prototype._readJson = function() {
|
684 | var that = this;
|
685 |
|
686 | if (this._json) {
|
687 | return Q.resolve(this._json);
|
688 | }
|
689 |
|
690 | return Q.fcall(function() {
|
691 |
|
692 | return fs.readFileSync(path.join(that._config.cwd, 'package.json'));
|
693 | })
|
694 | .then(function(buffer) {
|
695 |
|
696 | var defaults = {},
|
697 | npm = JSON.parse(buffer.toString());
|
698 |
|
699 | defaults.name =
|
700 | npm.name || path.basename(that._config.cwd) || 'root';
|
701 | defaults.description = npm.description;
|
702 | defaults.main = npm.main;
|
703 | defaults.authors = npm.contributors || npm.author;
|
704 | defaults.license = npm.license;
|
705 | defaults.keywords = npm.keywords;
|
706 |
|
707 | return defaults;
|
708 | })
|
709 | .catch(function(err) {
|
710 |
|
711 | return { name: path.basename(that._config.cwd) || 'root' };
|
712 | })
|
713 | .then(function(defaults) {
|
714 | return (that._json = readJson(that._config.cwd, {
|
715 | assume: defaults,
|
716 | logger: that._logger
|
717 | }));
|
718 | })
|
719 | .spread(function(json, deprecated, assumed) {
|
720 | var jsonStr;
|
721 |
|
722 | if (deprecated) {
|
723 | that._logger.warn(
|
724 | 'deprecated',
|
725 | 'You are using the deprecated ' + deprecated + ' file'
|
726 | );
|
727 | }
|
728 |
|
729 | if (!assumed) {
|
730 | that._jsonFile = path.join(
|
731 | that._config.cwd,
|
732 | deprecated ? deprecated : 'bower.json'
|
733 | );
|
734 | }
|
735 |
|
736 | jsonStr = JSON.stringify(json, null, ' ') + '\n';
|
737 | that._jsonHash = md5(jsonStr);
|
738 | return (that._json = json);
|
739 | });
|
740 | };
|
741 |
|
742 | Project.prototype._readInstalled = function() {
|
743 | var componentsDir;
|
744 | var that = this;
|
745 |
|
746 | if (this._installed) {
|
747 | return Q.resolve(this._installed);
|
748 | }
|
749 |
|
750 |
|
751 |
|
752 | componentsDir = relativeToBaseDir(this._config.cwd)(this._config.directory);
|
753 | return (this._installed = Q.nfcall(glob, '*/.bower.json', {
|
754 | cwd: componentsDir,
|
755 | dot: true
|
756 | }).then(function(filenames) {
|
757 | var promises;
|
758 | var decEndpoints = {};
|
759 |
|
760 |
|
761 | promises = filenames.map(function(filename) {
|
762 | var name = path.dirname(filename);
|
763 | var metaFile = path.join(componentsDir, filename);
|
764 |
|
765 |
|
766 | return readJson(metaFile).spread(function(pkgMeta) {
|
767 | decEndpoints[name] = {
|
768 | name: name,
|
769 | source: pkgMeta._originalSource || pkgMeta._source,
|
770 | target: pkgMeta._target,
|
771 | canonicalDir: path.dirname(metaFile),
|
772 | pkgMeta: pkgMeta
|
773 | };
|
774 | });
|
775 | });
|
776 |
|
777 |
|
778 |
|
779 | return Q.all(promises).then(function() {
|
780 | return (that._installed = decEndpoints);
|
781 | });
|
782 | }));
|
783 | };
|
784 |
|
785 | Project.prototype._readLinks = function() {
|
786 | var componentsDir;
|
787 | var that = this;
|
788 |
|
789 |
|
790 | componentsDir = relativeToBaseDir(this._config.cwd)(this._config.directory);
|
791 | return Q.nfcall(fs.readdir, componentsDir).then(
|
792 | function(filenames) {
|
793 | var promises;
|
794 | var decEndpoints = {};
|
795 |
|
796 | promises = filenames.map(function(filename) {
|
797 | var dir = path.join(componentsDir, filename);
|
798 |
|
799 |
|
800 | return validLink(dir).spread(function(valid, err) {
|
801 | var name;
|
802 |
|
803 | if (!valid) {
|
804 | if (err) {
|
805 | that._logger.debug(
|
806 | 'read-link',
|
807 | 'Link ' + dir + ' is invalid',
|
808 | {
|
809 | filename: dir,
|
810 | error: err
|
811 | }
|
812 | );
|
813 | }
|
814 | return;
|
815 | }
|
816 |
|
817 |
|
818 | if (!valid.isDirectory()) {
|
819 | return;
|
820 | }
|
821 |
|
822 | name = path.basename(dir);
|
823 | return readJson(dir, {
|
824 | assume: { name: name }
|
825 | }).spread(function(json, deprecated) {
|
826 | if (deprecated) {
|
827 | that._logger.warn(
|
828 | 'deprecated',
|
829 | 'Package ' +
|
830 | name +
|
831 | ' is using the deprecated ' +
|
832 | deprecated
|
833 | );
|
834 | }
|
835 |
|
836 | json._direct = true;
|
837 | decEndpoints[name] = {
|
838 | name: name,
|
839 | source: dir,
|
840 | target: '*',
|
841 | canonicalDir: dir,
|
842 | pkgMeta: json,
|
843 | linked: true
|
844 | };
|
845 | });
|
846 | });
|
847 | });
|
848 |
|
849 |
|
850 |
|
851 | return Q.all(promises).then(function() {
|
852 | return decEndpoints;
|
853 | });
|
854 |
|
855 | },
|
856 | function(err) {
|
857 | if (err.code !== 'ENOENT') {
|
858 | throw err;
|
859 | }
|
860 |
|
861 | return {};
|
862 | }
|
863 | );
|
864 | };
|
865 |
|
866 | Project.prototype._removePackages = function(packages) {
|
867 | var that = this;
|
868 | var promises = [];
|
869 |
|
870 | return (
|
871 | scripts
|
872 | .preuninstall(
|
873 | that._config,
|
874 | that._logger,
|
875 | packages,
|
876 | that._installed,
|
877 | that._json
|
878 | )
|
879 | .then(function() {
|
880 | mout.object.forOwn(packages, function(dir, name) {
|
881 | var promise;
|
882 |
|
883 |
|
884 | if (!dir) {
|
885 | promise = Q.resolve();
|
886 | that._logger.warn(
|
887 | 'not-installed',
|
888 | "'" +
|
889 | name +
|
890 | "'" +
|
891 | ' cannot be uninstalled as it is not currently installed',
|
892 | {
|
893 | name: name
|
894 | }
|
895 | );
|
896 | } else {
|
897 | promise = Q.nfcall(rimraf, dir);
|
898 | that._logger.action('uninstall', name, {
|
899 | name: name,
|
900 | dir: dir
|
901 | });
|
902 | }
|
903 |
|
904 |
|
905 | if (
|
906 | (that._options.save || that._config.save) &&
|
907 | that._json.dependencies
|
908 | ) {
|
909 | promise = promise.then(function() {
|
910 | delete that._json.dependencies[name];
|
911 | });
|
912 | }
|
913 |
|
914 | if (that._options.saveDev && that._json.devDependencies) {
|
915 | promise = promise.then(function() {
|
916 | delete that._json.devDependencies[name];
|
917 | });
|
918 | }
|
919 |
|
920 | promises.push(promise);
|
921 | });
|
922 |
|
923 | return Q.all(promises);
|
924 | })
|
925 | .then(function() {
|
926 | return that.saveJson();
|
927 | })
|
928 |
|
929 | .then(
|
930 | scripts.postuninstall.bind(
|
931 | null,
|
932 | that._config,
|
933 | that._logger,
|
934 | packages,
|
935 | that._installed,
|
936 | that._json
|
937 | )
|
938 | )
|
939 |
|
940 | .then(function() {
|
941 | return mout.object.filter(packages, function(dir) {
|
942 | return !!dir;
|
943 | });
|
944 | })
|
945 | );
|
946 | };
|
947 |
|
948 | Project.prototype._restoreNode = function(node, flattened, jsonKey, processed) {
|
949 | var deps;
|
950 |
|
951 |
|
952 | if (node.missing) {
|
953 | return;
|
954 | }
|
955 |
|
956 | node.dependencies = node.dependencies || {};
|
957 | node.dependants = node.dependants || {};
|
958 | processed = processed || {};
|
959 |
|
960 |
|
961 | deps = mout.object.filter(node.pkgMeta[jsonKey], function(value, key) {
|
962 | return !processed[node.name + ':' + key];
|
963 | });
|
964 |
|
965 | mout.object.forOwn(
|
966 | deps,
|
967 | function(value, key) {
|
968 | var local = flattened[key];
|
969 | var json = endpointParser.json2decomposed(key, value);
|
970 | var restored;
|
971 | var compatible;
|
972 | var originalSource;
|
973 |
|
974 |
|
975 | if (!local) {
|
976 | flattened[key] = restored = json;
|
977 | restored.missing = true;
|
978 |
|
979 |
|
980 |
|
981 | } else {
|
982 | compatible =
|
983 | local.linked ||
|
984 | (!local.missing && json.target === local.pkgMeta._target);
|
985 |
|
986 | if (!compatible) {
|
987 | restored = json;
|
988 |
|
989 | if (!local.missing) {
|
990 | restored.pkgMeta = local.pkgMeta;
|
991 | restored.canonicalDir = local.canonicalDir;
|
992 | restored.incompatible = true;
|
993 | } else {
|
994 | restored.missing = true;
|
995 | }
|
996 | } else {
|
997 | restored = local;
|
998 | mout.object.mixIn(local, json);
|
999 | }
|
1000 |
|
1001 |
|
1002 |
|
1003 | if (node.root && compatible) {
|
1004 | originalSource = mout.object.get(
|
1005 | local,
|
1006 | 'pkgMeta._originalSource'
|
1007 | );
|
1008 | if (originalSource && originalSource !== json.source) {
|
1009 | restored.different = true;
|
1010 | }
|
1011 | }
|
1012 | }
|
1013 |
|
1014 |
|
1015 | node.dependencies[key] = restored;
|
1016 | processed[node.name + ':' + key] = true;
|
1017 |
|
1018 | restored.dependants = restored.dependants || {};
|
1019 | restored.dependants[node.name] = mout.object.mixIn({}, node);
|
1020 |
|
1021 |
|
1022 | this._restoreNode(restored, flattened, 'dependencies', processed);
|
1023 |
|
1024 |
|
1025 | if (local && restored !== local) {
|
1026 | this._restoreNode(local, flattened, 'dependencies', processed);
|
1027 | }
|
1028 | },
|
1029 | this
|
1030 | );
|
1031 | };
|
1032 |
|
1033 | module.exports = Project;
|