Skip to content

Commit

Permalink
Update: Correct symlink/junction behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
erikkemperman committed Sep 29, 2017
1 parent d8625ff commit c40d603
Show file tree
Hide file tree
Showing 11 changed files with 153 additions and 93 deletions.
14 changes: 4 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,11 @@ Default: `false`
##### `options.useJunctions`

When creating a symlink, whether or not a directory symlink should be created as a `junction`.
This option is only relevant on Windows and ignored elsewhere.

Type: `Boolean`

Default: `true` on Windows, `false` on all other platforms
Default: `true`

### `symlink(folder[, options])`

Expand All @@ -239,14 +240,6 @@ Type: `String`

Default: `process.cwd()`

##### `options.mode`

The mode the symlinks should be created with.

Type: `Number`

Default: The `mode` of the input file (`file.stat.mode`) if any, or the process mode if the input file has no `mode` property.

##### `options.dirMode`

The mode the directory should be created with.
Expand Down Expand Up @@ -274,10 +267,11 @@ Default: `false`
##### `options.useJunctions`

Whether or not a directory symlink should be created as a `junction`.
This option is only relevant on Windows and ignored elsewhere.

Type: `Boolean`

Default: `true` on Windows, `false` on all other platforms
Default: `true`

[glob-stream]: https://github.com/gulpjs/glob-stream
[node-glob]: https://github.com/isaacs/node-glob
Expand Down
1 change: 1 addition & 0 deletions lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

module.exports = {
MASK_MODE: parseInt('7777', 8),
LINK_MODE: parseInt('120777', 8),
DEFAULT_FILE_MODE: parseInt('0666', 8),
};
6 changes: 1 addition & 5 deletions lib/dest/options.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
'use strict';

var os = require('os');

var isWindows = (os.platform() === 'win32');

var config = {
cwd: {
type: 'string',
Expand Down Expand Up @@ -36,7 +32,7 @@ var config = {
// Symlink options
useJunctions: {
type: 'boolean',
default: isWindows,
default: true,
},
relativeSymlinks: {
type: 'boolean',
Expand Down
13 changes: 10 additions & 3 deletions lib/dest/prepare.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ var path = require('path');
var fs = require('graceful-fs');
var through = require('through2');

var constants = require('../constants');

function prepareWrite(folderResolver, optResolver) {
if (!folderResolver) {
throw new Error('Invalid output folder');
}

function normalize(file, enc, cb) {
var mode = optResolver.resolve('mode', file);
var cwd = path.resolve(optResolver.resolve('cwd', file));

var outFolderPath = folderResolver.resolve('outFolder', file);
Expand All @@ -22,8 +23,14 @@ function prepareWrite(folderResolver, optResolver) {
var writePath = path.resolve(basePath, file.relative);

// Wire up new properties
file.stat = (file.stat || new fs.Stats());
file.stat.mode = mode;
if (file.symlink) {
file.stat = new fs.Stats();
file.stat.mode = constants.LINK_MODE;
} else {
file.stat = (file.stat || new fs.Stats());
file.stat.mode = optResolver.resolve('mode', file);;

}
file.cwd = cwd;
file.base = basePath;
file.path = writePath;
Expand Down
42 changes: 28 additions & 14 deletions lib/dest/write-contents/write-symbolic-link.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
'use strict';

var os = require('os');
var path = require('path');

var fs = require('graceful-fs');

var fo = require('../../file-operations');

function writeSymbolicLink(file, optResolver, onWritten) {
var isDirectory = file.isDirectory();
var isWindows = (os.platform() === 'win32');

function writeSymbolicLink(file, optResolver, onWritten) {
// This option provides a way to create a Junction instead of a
// Directory symlink on Windows. This comes with the following caveats:
// * NTFS Junctions cannot be relative.
Expand All @@ -18,25 +21,36 @@ function writeSymbolicLink(file, optResolver, onWritten) {
// For example, JetBrains product lines will delete the entire
// contents of the TARGET directory because the product does not
// realize it's a symlink as the JVM and Node return false for isSymlink.
var useJunctions = optResolver.resolve('useJunctions', file);
var useJunctions = isWindows && optResolver.resolve('useJunctions', file);

var symDirType = useJunctions ? 'junction' : 'dir';
var symType = isDirectory ? symDirType : 'file';
var isRelative = optResolver.resolve('relativeSymlinks', file);
var flag = optResolver.resolve('flag', file);

// This is done after prepare() to use the adjusted file.base property
if (isRelative && symType !== 'junction') {
file.symlink = path.relative(file.base, file.symlink);
if (useJunctions) {
fs.stat(file.symlink, onStat);
} else {
onStat();
}

var flag = optResolver.resolve('flag', file);
function onStat(statErr, stat) {
if (statErr) {
return onWritten(statErr);
}

var symDirType = useJunctions ? 'junction' : 'dir';
var symType = stat && stat.isDirectory() ? symDirType : 'file';

var opts = {
flag: flag,
type: symType,
};
// This is done after prepare() to use the adjusted file.base property
if (isRelative && symType !== 'junction') {
file.symlink = path.relative(file.base, file.symlink);
}

fo.symlink(file.symlink, file.path, opts, onWritten);
var opts = {
flag: flag,
type: symType,
};
fo.symlink(file.symlink, file.path, opts, onWritten);
}
}

module.exports = writeSymbolicLink;
41 changes: 27 additions & 14 deletions lib/symlink/link-file.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
'use strict';

var os = require('os');
var path = require('path');

var fs = require('graceful-fs');
var through = require('through2');

var fo = require('../file-operations');

var isWindows = (os.platform() === 'win32');

function linkStream(optResolver) {

function linkFile(file, enc, callback) {
var isDirectory = file.isDirectory();

// This option provides a way to create a Junction instead of a
// Directory symlink on Windows. This comes with the following caveats:
// * NTFS Junctions cannot be relative.
Expand All @@ -22,25 +24,36 @@ function linkStream(optResolver) {
// For example, JetBrains product lines will delete the entire
// contents of the TARGET directory because the product does not
// realize it's a symlink as the JVM and Node return false for isSymlink.
var useJunctions = optResolver.resolve('useJunctions', file);
var useJunctions = isWindows && optResolver.resolve('useJunctions', file);

var symDirType = useJunctions ? 'junction' : 'dir';
var symType = isDirectory ? symDirType : 'file';
var isRelative = optResolver.resolve('relativeSymlinks', file);
var flag = optResolver.resolve('flag', file);

// This is done after prepare() to use the adjusted file.base property
if (isRelative && symType !== 'junction') {
file.symlink = path.relative(file.base, file.symlink);
if (useJunctions) {
fs.stat(file.symlink, onStat);
} else {
onStat();
}

var flag = optResolver.resolve('flag', file);
function onStat(statErr, stat) {
if (statErr) {
return callback(statErr);
}

var symDirType = useJunctions ? 'junction' : 'dir';
var symType = stat && stat.isDirectory() ? symDirType : 'file';

var opts = {
flag: flag,
type: symType,
};
// This is done after prepare() to use the adjusted file.base property
if (isRelative && symType !== 'junction') {
file.symlink = path.relative(file.base, file.symlink);
}

fo.symlink(file.symlink, file.path, opts, onSymlink);
var opts = {
flag: flag,
type: symType,
};
fo.symlink(file.symlink, file.path, opts, onSymlink);
}

function onSymlink(symlinkErr) {
if (symlinkErr) {
Expand Down
12 changes: 1 addition & 11 deletions lib/symlink/options.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
'use strict';

var os = require('os');

var isWindows = (os.platform() === 'win32');

var config = {
useJunctions: {
type: 'boolean',
default: isWindows,
default: true,
},
relativeSymlinks: {
type: 'boolean',
Expand All @@ -17,12 +13,6 @@ var config = {
type: 'string',
default: process.cwd,
},
mode: {
type: 'number',
default: function(file) {
return file.stat ? file.stat.mode : null;
},
},
dirMode: {
type: 'number',
},
Expand Down
9 changes: 4 additions & 5 deletions lib/symlink/prepare.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
'use strict';

// TODO: currently a copy-paste of prepareWrite but should be customized

var path = require('path');

var fs = require('graceful-fs');
var through = require('through2');

var constants = require('../constants');

function prepareSymlink(folderResolver, optResolver) {
if (!folderResolver) {
throw new Error('Invalid output folder');
}

function normalize(file, enc, cb) {
var mode = optResolver.resolve('mode', file);
var cwd = path.resolve(optResolver.resolve('cwd', file));

var outFolderPath = folderResolver.resolve('outFolder', file);
Expand All @@ -24,8 +23,8 @@ function prepareSymlink(folderResolver, optResolver) {
var writePath = path.resolve(basePath, file.relative);

// Wire up new properties
file.stat = (file.stat || new fs.Stats());
file.stat.mode = mode;
file.stat = new fs.Stats();
file.stat.mode = constants.LINK_MODE;
file.cwd = cwd;
file.base = basePath;
file.symlink = file.path;
Expand Down
4 changes: 4 additions & 0 deletions test/dest-symlinks.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ describe('.dest() with symlinks', function() {
expect(files.length).toEqual(1);
expect(file.symlink).toEqual(symlink);
expect(files[0].symlink).toEqual(symlink);
expect(files[0].stat).toExist();
expect(files[0].stat.isSymbolicLink()).toBe(true);
expect(files[0].path).toEqual(outputPath);
}

Expand All @@ -74,6 +76,8 @@ describe('.dest() with symlinks', function() {

expect(files.length).toEqual(1);
expect(outputLink).toEqual(path.normalize('../fixtures/test.txt'));
expect(files[0].stat).toExist();
expect(files[0].stat.isSymbolicLink()).toBe(true);
}

pipe([
Expand Down
Loading

0 comments on commit c40d603

Please sign in to comment.