create-symlink.js 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. "use strict";
  2. const cmdShim = require("cmd-shim");
  3. const fs = require("fs-extra");
  4. const log = require("npmlog");
  5. const path = require("path");
  6. module.exports.createSymlink = createSymlink;
  7. function createSymlink(src, dest, type) {
  8. log.silly("createSymlink", [src, dest, type]);
  9. if (process.platform === "win32") {
  10. return createWindowsSymlink(src, dest, type);
  11. }
  12. return createPosixSymlink(src, dest, type);
  13. }
  14. function createSymbolicLink(src, dest, type) {
  15. log.silly("createSymbolicLink", [src, dest, type]);
  16. return fs
  17. .lstat(dest)
  18. .then(() => fs.unlink(dest))
  19. .catch(() => {
  20. /* nothing exists at destination */
  21. })
  22. .then(() => fs.symlink(src, dest, type));
  23. }
  24. function createPosixSymlink(src, dest, _type) {
  25. const type = _type === "exec" ? "file" : _type;
  26. const relativeSymlink = path.relative(path.dirname(dest), src);
  27. if (_type === "exec") {
  28. // If the src exists, create a real symlink.
  29. // If the src doesn't exist yet, create a shim shell script.
  30. return fs.pathExists(src).then((exists) => {
  31. if (exists) {
  32. return createSymbolicLink(relativeSymlink, dest, type).then(() => fs.chmod(src, 0o755));
  33. }
  34. return shShim(src, dest, type).then(() => fs.chmod(dest, 0o755));
  35. });
  36. }
  37. return createSymbolicLink(relativeSymlink, dest, type);
  38. }
  39. function createWindowsSymlink(src, dest, type) {
  40. if (type === "exec") {
  41. // If the src exists, shim directly.
  42. // If the src doesn't exist yet, create a temp src so cmd-shim doesn't explode.
  43. return fs.pathExists(src).then((exists) => {
  44. if (exists) {
  45. return cmdShim(src, dest);
  46. }
  47. return fs
  48. .outputFile(src, "")
  49. .then(() => cmdShim(src, dest))
  50. .then(
  51. // fs.remove() never rejects
  52. () => fs.remove(src),
  53. (err) =>
  54. fs.remove(src).then(() => {
  55. // clean up, but don't swallow error
  56. throw err;
  57. })
  58. );
  59. });
  60. }
  61. return createSymbolicLink(src, dest, type);
  62. }
  63. function shShim(src, dest, type) {
  64. log.silly("shShim", [src, dest, type]);
  65. const absTarget = path.resolve(path.dirname(dest), src);
  66. const scriptLines = ["#!/bin/sh", `chmod +x ${absTarget} && exec ${absTarget} "$@"`];
  67. return fs.writeFile(dest, scriptLines.join("\n"));
  68. }