link-file.js 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. 'use strict';
  2. var os = require('os');
  3. var path = require('path');
  4. var through = require('through2');
  5. var fo = require('../file-operations');
  6. var isWindows = (os.platform() === 'win32');
  7. function linkStream(optResolver) {
  8. function linkFile(file, enc, callback) {
  9. var isRelative = optResolver.resolve('relativeSymlinks', file);
  10. var flags = fo.getFlags({
  11. overwrite: optResolver.resolve('overwrite', file),
  12. append: false,
  13. });
  14. if (!isWindows) {
  15. // On non-Windows, just use 'file'
  16. return createLinkWithType('file');
  17. }
  18. fo.reflectStat(file.symlink, file, onReflectTarget);
  19. function onReflectTarget(statErr) {
  20. if (statErr && statErr.code !== 'ENOENT') {
  21. return callback(statErr);
  22. }
  23. // If target doesn't exist, the vinyl will still carry the target stats.
  24. // Let's use those to determine which kind of dangling link to create.
  25. // This option provides a way to create a Junction instead of a
  26. // Directory symlink on Windows. This comes with the following caveats:
  27. // * NTFS Junctions cannot be relative.
  28. // * NTFS Junctions MUST be directories.
  29. // * NTFS Junctions must be on the same file system.
  30. // * Most products CANNOT detect a directory is a Junction:
  31. // This has the side effect of possibly having a whole directory
  32. // deleted when a product is deleting the Junction directory.
  33. // For example, JetBrains product lines will delete the entire contents
  34. // of the TARGET directory because the product does not realize it's
  35. // a symlink as the JVM and Node return false for isSymlink.
  36. // This function is Windows only, so we don't need to check again
  37. var useJunctions = optResolver.resolve('useJunctions', file);
  38. var dirType = useJunctions ? 'junction' : 'dir';
  39. var type = !statErr && file.isDirectory() ? dirType : 'file';
  40. createLinkWithType(type);
  41. }
  42. function createLinkWithType(type) {
  43. // This is done after prepare() to use the adjusted file.base property
  44. if (isRelative && type !== 'junction') {
  45. file.symlink = path.relative(file.base, file.symlink);
  46. }
  47. var opts = {
  48. flags: flags,
  49. type: type,
  50. };
  51. fo.symlink(file.symlink, file.path, opts, onSymlink);
  52. }
  53. function onSymlink(symlinkErr) {
  54. if (symlinkErr) {
  55. return callback(symlinkErr);
  56. }
  57. fo.reflectLinkStat(file.path, file, onReflectLink);
  58. }
  59. function onReflectLink(reflectErr) {
  60. if (reflectErr) {
  61. return callback(reflectErr);
  62. }
  63. callback(null, file);
  64. }
  65. }
  66. return through.obj(linkFile);
  67. }
  68. module.exports = linkStream;