mirror of
https://github.com/shelljs/shelljs.git
synced 2026-01-18 16:03:37 +00:00
Merge pull request #322 from BYK/linking-windows
fix(Windows): fix symlinking on Windows
This commit is contained in:
commit
514e7b0c75
21
src/ln.js
21
src/ln.js
@ -1,7 +1,6 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var common = require('./common');
|
||||
var os = require('os');
|
||||
|
||||
//@
|
||||
//@ ### ln([options,] source, dest)
|
||||
@ -42,15 +41,29 @@ function _ln(options, source, dest) {
|
||||
}
|
||||
|
||||
if (options.symlink) {
|
||||
if ((isAbsolute && !fs.existsSync(sourcePath)) || !fs.existsSync(path.resolve(process.cwd(), path.dirname(dest), source))) {
|
||||
var isWindows = common.platform === 'win';
|
||||
var linkType = isWindows ? 'file' : null;
|
||||
var resolvedSourcePath = isAbsolute ? sourcePath : path.resolve(process.cwd(), path.dirname(dest), source);
|
||||
if (!fs.existsSync(resolvedSourcePath)) {
|
||||
common.error('Source file does not exist', true);
|
||||
} else if (isWindows && fs.statSync(resolvedSourcePath).isDirectory()) {
|
||||
linkType = 'junction';
|
||||
}
|
||||
|
||||
try {
|
||||
fs.symlinkSync(linkType === 'junction' ? resolvedSourcePath: source, dest, linkType);
|
||||
} catch (err) {
|
||||
common.error(err.message);
|
||||
}
|
||||
fs.symlinkSync(source, dest, os.platform() === "win32" ? "junction" : null);
|
||||
} else {
|
||||
if (!fs.existsSync(source)) {
|
||||
common.error('Source file does not exist', true);
|
||||
}
|
||||
fs.linkSync(source, dest);
|
||||
try {
|
||||
fs.linkSync(source, dest);
|
||||
} catch (err) {
|
||||
common.error(err.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
module.exports = _ln;
|
||||
|
||||
128
test/ln.js
128
test/ln.js
@ -1,5 +1,6 @@
|
||||
var shell = require('..');
|
||||
var common = require('../src/common');
|
||||
var isWindows = common.platform === 'win';
|
||||
|
||||
var assert = require('assert'),
|
||||
fs = require('fs'),
|
||||
@ -7,6 +8,19 @@ var assert = require('assert'),
|
||||
|
||||
shell.config.silent = true;
|
||||
|
||||
// On Windows, symlinks for files need admin permissions. This helper
|
||||
// skips certain tests if we are on Windows and got an EPERM error
|
||||
function skipOnWinForEPERM (action, test) {
|
||||
action();
|
||||
var error = shell.error();
|
||||
|
||||
if (isWindows && error && /EPERM:/.test(error)) {
|
||||
console.log("Got EPERM when testing symlinks on Windows. Assuming non-admin environment and skipping test.");
|
||||
} else {
|
||||
test();
|
||||
}
|
||||
}
|
||||
|
||||
shell.rm('-rf', 'tmp');
|
||||
shell.mkdir('tmp');
|
||||
|
||||
@ -45,13 +59,6 @@ assert.ok(shell.error());
|
||||
// Valids
|
||||
//
|
||||
|
||||
// On Windows, symlinks for files need admin permissions.
|
||||
// It is also broken now since current implementation simply uses `'junction'` type which is only
|
||||
// valid for directories.
|
||||
// TODO: Fix this for Windows and also add symlink tests for directories
|
||||
if (common.platform === 'win')
|
||||
shell.exit(123);
|
||||
|
||||
shell.ln('tmp/file1', 'tmp/linkfile1');
|
||||
assert(fs.existsSync('tmp/linkfile1'));
|
||||
assert.equal(
|
||||
@ -64,17 +71,25 @@ assert.equal(
|
||||
'new content 1'
|
||||
);
|
||||
|
||||
shell.ln('-s', 'file2', 'tmp/linkfile2');
|
||||
assert(fs.existsSync('tmp/linkfile2'));
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/file2').toString(),
|
||||
fs.readFileSync('tmp/linkfile2').toString()
|
||||
);
|
||||
fs.writeFileSync('tmp/file2', 'new content 2');
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/linkfile2').toString(),
|
||||
'new content 2'
|
||||
);
|
||||
skipOnWinForEPERM(shell.ln.bind(shell, '-s', 'file2', 'tmp/linkfile2'), function () {
|
||||
assert(fs.existsSync('tmp/linkfile2'));
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/file2').toString(),
|
||||
fs.readFileSync('tmp/linkfile2').toString()
|
||||
);
|
||||
fs.writeFileSync('tmp/file2', 'new content 2');
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/linkfile2').toString(),
|
||||
'new content 2'
|
||||
);
|
||||
});
|
||||
|
||||
// Symbolic link directory test
|
||||
shell.mkdir('tmp/ln');
|
||||
shell.touch('tmp/ln/hello');
|
||||
shell.ln('-s', 'ln', 'tmp/dir1');
|
||||
assert(fs.existsSync('tmp/ln/hello'));
|
||||
assert(fs.existsSync('tmp/dir1/hello'));
|
||||
|
||||
shell.ln('-f', 'tmp/file1.js', 'tmp/file2.js');
|
||||
assert(fs.existsSync('tmp/file2.js'));
|
||||
@ -88,46 +103,49 @@ assert.equal(
|
||||
'new content js'
|
||||
);
|
||||
|
||||
shell.ln('-sf', 'file1.txt', 'tmp/file2.txt');
|
||||
assert(fs.existsSync('tmp/file2.txt'));
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/file1.txt').toString(),
|
||||
fs.readFileSync('tmp/file2.txt').toString()
|
||||
);
|
||||
fs.writeFileSync('tmp/file1.txt', 'new content txt');
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/file2.txt').toString(),
|
||||
'new content txt'
|
||||
);
|
||||
skipOnWinForEPERM(shell.ln.bind(shell, '-sf', 'file1.txt', 'tmp/file2.txt'), function () {
|
||||
assert(fs.existsSync('tmp/file2.txt'));
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/file1.txt').toString(),
|
||||
fs.readFileSync('tmp/file2.txt').toString()
|
||||
);
|
||||
fs.writeFileSync('tmp/file1.txt', 'new content txt');
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/file2.txt').toString(),
|
||||
'new content txt'
|
||||
);
|
||||
});
|
||||
|
||||
// Abspath regression
|
||||
shell.ln('-sf', 'file1', path.resolve('tmp/abspath'));
|
||||
assert(fs.existsSync('tmp/abspath'));
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/file1').toString(),
|
||||
fs.readFileSync('tmp/abspath').toString()
|
||||
);
|
||||
fs.writeFileSync('tmp/file1', 'new content 3');
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/abspath').toString(),
|
||||
'new content 3'
|
||||
);
|
||||
skipOnWinForEPERM(shell.ln.bind(shell, '-sf', 'file1', path.resolve('tmp/abspath')), function () {
|
||||
assert(fs.existsSync('tmp/abspath'));
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/file1').toString(),
|
||||
fs.readFileSync('tmp/abspath').toString()
|
||||
);
|
||||
fs.writeFileSync('tmp/file1', 'new content 3');
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/abspath').toString(),
|
||||
'new content 3'
|
||||
);
|
||||
});
|
||||
|
||||
// Relative regression
|
||||
shell.ln('-sf', 'file1.txt', 'tmp/file2.txt');
|
||||
shell.mkdir('-p', 'tmp/new');
|
||||
// Move the symlink first, as the reverse confuses `mv`.
|
||||
shell.mv('tmp/file2.txt', 'tmp/new/file2.txt');
|
||||
shell.mv('tmp/file1.txt', 'tmp/new/file1.txt');
|
||||
assert(fs.existsSync('tmp/new/file2.txt'));
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/new/file1.txt').toString(),
|
||||
fs.readFileSync('tmp/new/file2.txt').toString()
|
||||
);
|
||||
fs.writeFileSync('tmp/new/file1.txt', 'new content txt');
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/new/file2.txt').toString(),
|
||||
'new content txt'
|
||||
);
|
||||
skipOnWinForEPERM(shell.ln.bind(shell, '-sf', 'file1.txt', 'tmp/file2.txt'), function () {
|
||||
shell.mkdir('-p', 'tmp/new');
|
||||
// Move the symlink first, as the reverse confuses `mv`.
|
||||
shell.mv('tmp/file2.txt', 'tmp/new/file2.txt');
|
||||
shell.mv('tmp/file1.txt', 'tmp/new/file1.txt');
|
||||
assert(fs.existsSync('tmp/new/file2.txt'));
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/new/file1.txt').toString(),
|
||||
fs.readFileSync('tmp/new/file2.txt').toString()
|
||||
);
|
||||
fs.writeFileSync('tmp/new/file1.txt', 'new content txt');
|
||||
assert.equal(
|
||||
fs.readFileSync('tmp/new/file2.txt').toString(),
|
||||
'new content txt'
|
||||
);
|
||||
});
|
||||
|
||||
shell.exit(123);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user