symlink-dependencies.js 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. "use strict";
  2. const fs = require("fs-extra");
  3. const pMap = require("p-map");
  4. const pMapSeries = require("p-map-series");
  5. const path = require("path");
  6. const { createSymlink } = require("@lerna/create-symlink");
  7. const { resolveSymlink } = require("@lerna/resolve-symlink");
  8. const { symlinkBinary } = require("@lerna/symlink-binary");
  9. module.exports.symlinkDependencies = symlinkDependencies;
  10. /**
  11. * Symlink all packages to the packages/node_modules directory
  12. * Symlink package binaries to dependent packages' node_modules/.bin directory
  13. * @param {Array.<Package>} packages
  14. * @param {Object} packageGraph
  15. * @param {Tracker} tracker
  16. * @returns {Promise}
  17. */
  18. function symlinkDependencies(packages, packageGraph, tracker) {
  19. tracker.info("", "Symlinking packages and binaries");
  20. tracker.addWork(packages.length);
  21. const nodes =
  22. packageGraph.size === packages.length
  23. ? packageGraph.values()
  24. : new Set(packages.map(({ name }) => packageGraph.get(name)));
  25. return pMapSeries(nodes, (currentNode) => {
  26. const currentName = currentNode.name;
  27. const currentNodeModules = currentNode.pkg.nodeModulesLocation;
  28. return pMap(currentNode.localDependencies, ([dependencyName, resolved]) => {
  29. if (resolved.type === "directory") {
  30. // a local file: specifier is already a symlink
  31. return;
  32. }
  33. // get PackageGraphNode of dependency
  34. // const dependencyName = resolved.name;
  35. const dependencyNode = packageGraph.get(dependencyName);
  36. const targetDirectory = path.join(currentNodeModules, dependencyName);
  37. let chain = Promise.resolve();
  38. // check if dependency is already installed
  39. chain = chain.then(() => fs.pathExists(targetDirectory));
  40. chain = chain.then((dirExists) => {
  41. if (dirExists) {
  42. const isDepSymlink = resolveSymlink(targetDirectory);
  43. if (isDepSymlink !== false && isDepSymlink !== dependencyNode.location) {
  44. // installed dependency is a symlink pointing to a different location
  45. tracker.warn(
  46. "EREPLACE_OTHER",
  47. `Symlink already exists for ${dependencyName} dependency of ${currentName}, ` +
  48. "but links to different location. Replacing with updated symlink..."
  49. );
  50. } else if (isDepSymlink === false) {
  51. // installed dependency is not a symlink
  52. tracker.warn(
  53. "EREPLACE_EXIST",
  54. `${dependencyName} is already installed for ${currentName}. Replacing with symlink...`
  55. );
  56. // remove installed dependency
  57. return fs.remove(targetDirectory);
  58. }
  59. } else {
  60. // ensure destination directory exists (dealing with scoped subdirs)
  61. return fs.ensureDir(path.dirname(targetDirectory));
  62. }
  63. });
  64. // create package symlink
  65. const dependencyLocation = dependencyNode.pkg.contents
  66. ? path.resolve(dependencyNode.location, dependencyNode.pkg.contents)
  67. : dependencyNode.location;
  68. chain = chain.then(() => createSymlink(dependencyLocation, targetDirectory, "junction"));
  69. // TODO: pass PackageGraphNodes directly instead of Packages
  70. chain = chain.then(() => symlinkBinary(dependencyNode.pkg, currentNode.pkg));
  71. return chain;
  72. }).then(() => {
  73. tracker.silly("actions", "finished", currentName);
  74. tracker.completeWork(1);
  75. });
  76. }).finally(() => tracker.finish());
  77. }