UNPKG

18.5 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6
7var _asyncToGenerator2;
8
9function _load_asyncToGenerator() {
10 return _asyncToGenerator2 = _interopRequireDefault(require('babel-runtime/helpers/asyncToGenerator'));
11}
12
13var _config;
14
15function _load_config() {
16 return _config = _interopRequireDefault(require('./config.js'));
17}
18
19var _executeLifecycleScript;
20
21function _load_executeLifecycleScript() {
22 return _executeLifecycleScript = _interopRequireDefault(require('./util/execute-lifecycle-script.js'));
23}
24
25var _crypto;
26
27function _load_crypto() {
28 return _crypto = _interopRequireWildcard(require('./util/crypto.js'));
29}
30
31var _fs;
32
33function _load_fs() {
34 return _fs = _interopRequireWildcard(require('./util/fs.js'));
35}
36
37var _packageNameUtils;
38
39function _load_packageNameUtils() {
40 return _packageNameUtils = require('./util/package-name-utils.js');
41}
42
43var _pack;
44
45function _load_pack() {
46 return _pack = require('./cli/commands/pack.js');
47}
48
49function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
50
51function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
52
53const fs = require('fs');
54
55const invariant = require('invariant');
56const path = require('path');
57
58const INSTALL_STAGES = ['preinstall', 'install', 'postinstall'];
59
60class PackageInstallScripts {
61 constructor(config, resolver, force) {
62 this.installed = 0;
63 this.resolver = resolver;
64 this.reporter = config.reporter;
65 this.config = config;
66 this.force = force;
67 this.artifacts = {};
68 }
69
70 setForce(force) {
71 this.force = force;
72 }
73
74 setArtifacts(artifacts) {
75 this.artifacts = artifacts;
76 }
77
78 getArtifacts() {
79 return this.artifacts;
80 }
81
82 getInstallCommands(pkg) {
83 const scripts = pkg.scripts;
84 if (scripts) {
85 const cmds = [];
86 for (var _iterator = INSTALL_STAGES, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
87 var _ref;
88
89 if (_isArray) {
90 if (_i >= _iterator.length) break;
91 _ref = _iterator[_i++];
92 } else {
93 _i = _iterator.next();
94 if (_i.done) break;
95 _ref = _i.value;
96 }
97
98 const stage = _ref;
99
100 const cmd = scripts[stage];
101 if (cmd) {
102 cmds.push([stage, cmd]);
103 }
104 }
105 return cmds;
106 } else {
107 return [];
108 }
109 }
110
111 walk(loc) {
112 var _this = this;
113
114 return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () {
115 const files = yield (_fs || _load_fs()).walk(loc, null, new Set(_this.config.registryFolders));
116 const mtimes = new Map();
117 for (var _iterator2 = files, _isArray2 = Array.isArray(_iterator2), _i2 = 0, _iterator2 = _isArray2 ? _iterator2 : _iterator2[Symbol.iterator]();;) {
118 var _ref2;
119
120 if (_isArray2) {
121 if (_i2 >= _iterator2.length) break;
122 _ref2 = _iterator2[_i2++];
123 } else {
124 _i2 = _iterator2.next();
125 if (_i2.done) break;
126 _ref2 = _i2.value;
127 }
128
129 const file = _ref2;
130
131 mtimes.set(file.relative, file.mtime);
132 }
133 return mtimes;
134 })();
135 }
136
137 saveBuildArtifacts(loc, pkg, beforeFiles, spinner) {
138 var _this2 = this;
139
140 return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () {
141 const afterFiles = yield _this2.walk(loc);
142
143 // work out what files have been created/modified
144 const buildArtifacts = [];
145 for (var _iterator3 = afterFiles, _isArray3 = Array.isArray(_iterator3), _i3 = 0, _iterator3 = _isArray3 ? _iterator3 : _iterator3[Symbol.iterator]();;) {
146 var _ref4;
147
148 if (_isArray3) {
149 if (_i3 >= _iterator3.length) break;
150 _ref4 = _iterator3[_i3++];
151 } else {
152 _i3 = _iterator3.next();
153 if (_i3.done) break;
154 _ref4 = _i3.value;
155 }
156
157 const _ref3 = _ref4;
158 const file = _ref3[0];
159 const mtime = _ref3[1];
160
161 if (!beforeFiles.has(file) || beforeFiles.get(file) !== mtime) {
162 buildArtifacts.push(file);
163 }
164 }
165
166 if (!buildArtifacts.length) {
167 // nothing else to do here since we have no build artifacts
168 return;
169 }
170
171 // set build artifacts
172 const ref = pkg._reference;
173 invariant(ref, 'expected reference');
174 _this2.artifacts[`${pkg.name}@${pkg.version}`] = buildArtifacts;
175 })();
176 }
177
178 install(cmds, pkg, spinner) {
179 var _this3 = this;
180
181 return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () {
182 const ref = pkg._reference;
183 invariant(ref, 'expected reference');
184 const locs = ref.locations;
185
186 let updateProgress;
187
188 if (cmds.length > 0) {
189 updateProgress = function updateProgress(data) {
190 const dataStr = data.toString() // turn buffer into string
191 .trim(); // trim whitespace
192
193 invariant(spinner && spinner.tick, 'We should have spinner and its ticker here');
194 if (dataStr) {
195 spinner.tick(dataStr
196 // Only get the last line
197 .substr(dataStr.lastIndexOf('\n') + 1)
198 // change tabs to spaces as they can interfere with the console
199 .replace(/\t/g, ' '));
200 }
201 };
202 }
203
204 try {
205 for (var _iterator4 = cmds, _isArray4 = Array.isArray(_iterator4), _i4 = 0, _iterator4 = _isArray4 ? _iterator4 : _iterator4[Symbol.iterator]();;) {
206 var _ref6;
207
208 if (_isArray4) {
209 if (_i4 >= _iterator4.length) break;
210 _ref6 = _iterator4[_i4++];
211 } else {
212 _i4 = _iterator4.next();
213 if (_i4.done) break;
214 _ref6 = _i4.value;
215 }
216
217 const _ref5 = _ref6;
218 const stage = _ref5[0];
219 const cmd = _ref5[1];
220
221 yield Promise.all(locs.map((() => {
222 var _ref7 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) {
223 var _ref8 = yield (0, (_executeLifecycleScript || _load_executeLifecycleScript()).default)({
224 stage,
225 config: _this3.config,
226 cwd: loc,
227 cmd,
228 isInteractive: false,
229 updateProgress
230 });
231
232 const stdout = _ref8.stdout;
233
234 _this3.reporter.verbose(stdout);
235 });
236
237 return function (_x) {
238 return _ref7.apply(this, arguments);
239 };
240 })()));
241 }
242 } catch (err) {
243 err.message = `${locs.join(', ')}: ${err.message}`;
244
245 invariant(ref, 'expected reference');
246
247 if (ref.optional) {
248 ref.ignore = true;
249 ref.incompatible = true;
250 _this3.reporter.warn(_this3.reporter.lang('optionalModuleScriptFail', err.message));
251 _this3.reporter.info(_this3.reporter.lang('optionalModuleFail'));
252
253 // Cleanup node_modules
254 try {
255 yield Promise.all(locs.map((() => {
256 var _ref9 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) {
257 yield (_fs || _load_fs()).unlink(loc);
258 });
259
260 return function (_x2) {
261 return _ref9.apply(this, arguments);
262 };
263 })()));
264 } catch (e) {
265 _this3.reporter.error(_this3.reporter.lang('optionalModuleCleanupFail', e.message));
266 }
267 } else {
268 throw err;
269 }
270 }
271 })();
272 }
273
274 packageCanBeInstalled(pkg) {
275 const cmds = this.getInstallCommands(pkg);
276 if (!cmds.length) {
277 return false;
278 }
279 if (this.config.packBuiltPackages && pkg.prebuiltVariants) {
280 for (const variant in pkg.prebuiltVariants) {
281 if (pkg._remote && pkg._remote.reference && pkg._remote.reference.includes(variant)) {
282 return false;
283 }
284 }
285 }
286 const ref = pkg._reference;
287 invariant(ref, 'Missing package reference');
288 if (!ref.fresh && !this.force) {
289 // this package hasn't been touched
290 return false;
291 }
292
293 // Don't run lifecycle scripts for hoisted packages
294 if (!ref.locations.length) {
295 return false;
296 }
297
298 // we haven't actually written this module out
299 if (ref.ignore) {
300 return false;
301 }
302 return true;
303 }
304
305 runCommand(spinner, pkg) {
306 var _this4 = this;
307
308 return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () {
309 const cmds = _this4.getInstallCommands(pkg);
310 spinner.setPrefix(++_this4.installed, pkg.name);
311 yield _this4.install(cmds, pkg, spinner);
312 })();
313 }
314
315 // detect if there is a circularDependency in the dependency tree
316 detectCircularDependencies(root, seenManifests, pkg) {
317 const ref = pkg._reference;
318 invariant(ref, 'expected reference');
319
320 const deps = ref.dependencies;
321 for (var _iterator5 = deps, _isArray5 = Array.isArray(_iterator5), _i5 = 0, _iterator5 = _isArray5 ? _iterator5 : _iterator5[Symbol.iterator]();;) {
322 var _ref10;
323
324 if (_isArray5) {
325 if (_i5 >= _iterator5.length) break;
326 _ref10 = _iterator5[_i5++];
327 } else {
328 _i5 = _iterator5.next();
329 if (_i5.done) break;
330 _ref10 = _i5.value;
331 }
332
333 const dep = _ref10;
334
335 const pkgDep = this.resolver.getStrictResolvedPattern(dep);
336 if (seenManifests.has(pkgDep)) {
337 // there is a cycle but not with the root
338 continue;
339 }
340 seenManifests.add(pkgDep);
341 // found a dependency pointing to root
342 if (pkgDep == root) {
343 return true;
344 }
345 if (this.detectCircularDependencies(root, seenManifests, pkgDep)) {
346 return true;
347 }
348 }
349 return false;
350 }
351
352 // find the next package to be installed
353 findInstallablePackage(workQueue, installed) {
354 for (var _iterator6 = workQueue, _isArray6 = Array.isArray(_iterator6), _i6 = 0, _iterator6 = _isArray6 ? _iterator6 : _iterator6[Symbol.iterator]();;) {
355 var _ref11;
356
357 if (_isArray6) {
358 if (_i6 >= _iterator6.length) break;
359 _ref11 = _iterator6[_i6++];
360 } else {
361 _i6 = _iterator6.next();
362 if (_i6.done) break;
363 _ref11 = _i6.value;
364 }
365
366 const pkg = _ref11;
367
368 const ref = pkg._reference;
369 invariant(ref, 'expected reference');
370 const deps = ref.dependencies;
371
372 let dependenciesFulfilled = true;
373 for (var _iterator7 = deps, _isArray7 = Array.isArray(_iterator7), _i7 = 0, _iterator7 = _isArray7 ? _iterator7 : _iterator7[Symbol.iterator]();;) {
374 var _ref12;
375
376 if (_isArray7) {
377 if (_i7 >= _iterator7.length) break;
378 _ref12 = _iterator7[_i7++];
379 } else {
380 _i7 = _iterator7.next();
381 if (_i7.done) break;
382 _ref12 = _i7.value;
383 }
384
385 const dep = _ref12;
386
387 const pkgDep = this.resolver.getStrictResolvedPattern(dep);
388 if (!installed.has(pkgDep)) {
389 dependenciesFulfilled = false;
390 break;
391 }
392 }
393
394 // all dependencies are installed
395 if (dependenciesFulfilled) {
396 return pkg;
397 }
398
399 // detect circular dependency, mark this pkg as installable to break the circle
400 if (this.detectCircularDependencies(pkg, new Set(), pkg)) {
401 return pkg;
402 }
403 }
404 return null;
405 }
406
407 worker(spinner, workQueue, installed, waitQueue) {
408 var _this5 = this;
409
410 return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () {
411 while (workQueue.size > 0) {
412 // find a installable package
413 const pkg = _this5.findInstallablePackage(workQueue, installed);
414
415 // can't find a package to install, register into waitQueue
416 if (pkg == null) {
417 spinner.clear();
418 yield new Promise(function (resolve) {
419 return waitQueue.add(resolve);
420 });
421 continue;
422 }
423
424 // found a package to install
425 workQueue.delete(pkg);
426 if (_this5.packageCanBeInstalled(pkg)) {
427 yield _this5.runCommand(spinner, pkg);
428 }
429 installed.add(pkg);
430 for (var _iterator8 = waitQueue, _isArray8 = Array.isArray(_iterator8), _i8 = 0, _iterator8 = _isArray8 ? _iterator8 : _iterator8[Symbol.iterator]();;) {
431 var _ref13;
432
433 if (_isArray8) {
434 if (_i8 >= _iterator8.length) break;
435 _ref13 = _iterator8[_i8++];
436 } else {
437 _i8 = _iterator8.next();
438 if (_i8.done) break;
439 _ref13 = _i8.value;
440 }
441
442 const workerResolve = _ref13;
443
444 workerResolve();
445 }
446 waitQueue.clear();
447 }
448 })();
449 }
450
451 init(seedPatterns) {
452 var _this6 = this;
453
454 return (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* () {
455 const workQueue = new Set();
456 const installed = new Set();
457 const pkgs = _this6.resolver.getTopologicalManifests(seedPatterns);
458 let installablePkgs = 0;
459 // A map to keep track of what files exist before installation
460 const beforeFilesMap = new Map();
461 for (var _iterator9 = pkgs, _isArray9 = Array.isArray(_iterator9), _i9 = 0, _iterator9 = _isArray9 ? _iterator9 : _iterator9[Symbol.iterator]();;) {
462 var _ref14;
463
464 if (_isArray9) {
465 if (_i9 >= _iterator9.length) break;
466 _ref14 = _iterator9[_i9++];
467 } else {
468 _i9 = _iterator9.next();
469 if (_i9.done) break;
470 _ref14 = _i9.value;
471 }
472
473 const pkg = _ref14;
474
475 if (_this6.packageCanBeInstalled(pkg)) {
476 const ref = pkg._reference;
477 invariant(ref, 'expected reference');
478 yield Promise.all(ref.locations.map((() => {
479 var _ref19 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (loc) {
480 beforeFilesMap.set(loc, (yield _this6.walk(loc)));
481 installablePkgs += 1;
482 });
483
484 return function (_x6) {
485 return _ref19.apply(this, arguments);
486 };
487 })()));
488 }
489 workQueue.add(pkg);
490 }
491
492 const set = _this6.reporter.activitySet(installablePkgs, Math.min(installablePkgs, _this6.config.childConcurrency));
493
494 // waitQueue acts like a semaphore to allow workers to register to be notified
495 // when there are more work added to the work queue
496 const waitQueue = new Set();
497 yield Promise.all(set.spinners.map(function (spinner) {
498 return _this6.worker(spinner, workQueue, installed, waitQueue);
499 }));
500 // generate built package as prebuilt one for offline mirror
501 const offlineMirrorPath = _this6.config.getOfflineMirrorPath();
502 if (_this6.config.packBuiltPackages && offlineMirrorPath) {
503 for (var _iterator10 = pkgs, _isArray10 = Array.isArray(_iterator10), _i10 = 0, _iterator10 = _isArray10 ? _iterator10 : _iterator10[Symbol.iterator]();;) {
504 var _ref15;
505
506 if (_isArray10) {
507 if (_i10 >= _iterator10.length) break;
508 _ref15 = _iterator10[_i10++];
509 } else {
510 _i10 = _iterator10.next();
511 if (_i10.done) break;
512 _ref15 = _i10.value;
513 }
514
515 const pkg = _ref15;
516
517 if (_this6.packageCanBeInstalled(pkg)) {
518 let prebuiltPath = path.join(offlineMirrorPath, 'prebuilt');
519 yield (_fs || _load_fs()).mkdirp(prebuiltPath);
520 const prebuiltFilename = (0, (_packageNameUtils || _load_packageNameUtils()).getPlatformSpecificPackageFilename)(pkg);
521 prebuiltPath = path.join(prebuiltPath, prebuiltFilename + '.tgz');
522 const ref = pkg._reference;
523 invariant(ref, 'expected reference');
524 const builtPackagePaths = ref.locations;
525
526 yield Promise.all(builtPackagePaths.map((() => {
527 var _ref16 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (builtPackagePath) {
528 // don't use pack command, we want to avoid the file filters logic
529 const stream = yield (0, (_pack || _load_pack()).packWithIgnoreAndHeaders)(builtPackagePath);
530
531 const hash = yield new Promise(function (resolve, reject) {
532 const validateStream = new (_crypto || _load_crypto()).HashStream();
533 stream.pipe(validateStream).pipe(fs.createWriteStream(prebuiltPath)).on('error', reject).on('close', function () {
534 return resolve(validateStream.getHash());
535 });
536 });
537 pkg.prebuiltVariants = pkg.prebuiltVariants || {};
538 pkg.prebuiltVariants[prebuiltFilename] = hash;
539 });
540
541 return function (_x3) {
542 return _ref16.apply(this, arguments);
543 };
544 })()));
545 }
546 }
547 } else {
548 // cache all build artifacts
549 for (var _iterator11 = pkgs, _isArray11 = Array.isArray(_iterator11), _i11 = 0, _iterator11 = _isArray11 ? _iterator11 : _iterator11[Symbol.iterator]();;) {
550 var _ref17;
551
552 if (_isArray11) {
553 if (_i11 >= _iterator11.length) break;
554 _ref17 = _iterator11[_i11++];
555 } else {
556 _i11 = _iterator11.next();
557 if (_i11.done) break;
558 _ref17 = _i11.value;
559 }
560
561 const pkg = _ref17;
562
563 if (_this6.packageCanBeInstalled(pkg)) {
564 const ref = pkg._reference;
565 invariant(ref, 'expected reference');
566 const beforeFiles = ref.locations.map(function (loc) {
567 return beforeFilesMap.get(loc);
568 });
569 yield Promise.all(beforeFiles.map((() => {
570 var _ref18 = (0, (_asyncToGenerator2 || _load_asyncToGenerator()).default)(function* (b, index) {
571 invariant(b, 'files before installation should always be recorded');
572 yield _this6.saveBuildArtifacts(ref.locations[index], pkg, b, set.spinners[0]);
573 });
574
575 return function (_x4, _x5) {
576 return _ref18.apply(this, arguments);
577 };
578 })()));
579 }
580 }
581 }
582
583 set.end();
584 })();
585 }
586}
587exports.default = PackageInstallScripts;
\No newline at end of file