-
Notifications
You must be signed in to change notification settings - Fork 739
Allow ln destination to be an existing directory, fixes #832 #833
New issue
Have a question ab 8000 out this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3fe1a67
4008596
50e6ca8
582414d
9392c55
946ab48
c57f7ff
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,7 +35,19 @@ function _ln(options, source, dest) { | |
var isAbsolute = (path.resolve(source) === sourcePath); | ||
dest = path.resolve(process.cwd(), String(dest)); | ||
|
||
if (fs.existsSync(dest)) { | ||
if (fs.existsSync(dest) && common.statFollowLinks(dest).isDirectory()) { | ||
dest = path.join(dest, path.basename(sourcePath)); | ||
} | ||
|
||
var destinationExists; | ||
try { | ||
fs.lstatSync(dest); | ||
destinationExists = true; | ||
} catch (err) { | ||
destinationExists = false; | ||
} | ||
|
||
if (destinationExists) { | ||
if (!options.force) { | ||
common.error('Destination file exists', { continue: true }); | ||
} | ||
|
@@ -47,7 +59,16 @@ function _ln(options, source, dest) { | |
var isWindows = process.platform === 'win32'; | ||
var linkType = isWindows ? 'file' : null; | ||
var resolvedSourcePath = isAbsolute ? sourcePath : path.resolve(process.cwd(), path.dirname(dest), source); | ||
if (!fs.existsSync(resolvedSourcePath)) { | ||
|
||
var resolvedSourceExists; | ||
try { | ||
fs.lstatSync(resolvedSourcePath); | ||
resolvedSourceExists = true; | ||
} catch (err) { | ||
resolvedSourceExists = false; | ||
} | ||
|
||
if (!resolvedSourceExists) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, was this behavior ever correct with $ ls this-does-not-exist
ls: cannot access 'this-does-not-exist': No such file or directory
$ rm bad-link
$ ln -s this-does-not-exist bad-link
$ ls bad-link # it's highlighted link a broken link
bad-link I think, long term, the correct logic is @loilo which bug does this address? |
||
common.error('Source file does not exist', { continue: true }); | ||
} else if (isWindows && common.statFollowLinks(resolvedSourcePath).isDirectory()) { | ||
linkType = 'junction'; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ test.beforeEach(t => { | |
}); | ||
|
||
test.afterEach.always(t => { | ||
process.chdir(CWD); | ||
shell.rm('-rf', t.context.tmp); | ||
}); | ||
|
||
|
@@ -48,6 +49,13 @@ test('destination already exists', t => { | |
t.is(result.code, 1); | ||
}); | ||
|
||
test('destination already exists inside directory', t => { | ||
shell.cd(t.context.tmp); | ||
const result = shell.ln('-s', 'file1', './'); | ||
t.truthy(shell.error()); | ||
t.is(result.code, 1); | ||
}); | ||
|
||
test('non-existent source', t => { | ||
const result = shell.ln(`${t.context.tmp}/noexist`, `${t.context.tmp}/linkfile1`); | ||
t.truthy(shell.error()); | ||
|
@@ -134,7 +142,28 @@ test('To current directory', t => { | |
t.truthy(fs.existsSync('dest/testfile.txt')); | ||
t.truthy(fs.existsSync('dir1/insideDir.txt')); | ||
t.falsy(fs.existsSync('dest/insideDir.txt')); | ||
shell.cd('..'); | ||
}); | ||
|
||
test('Inside existing directory', t => { | ||
shell.cd(t.context.tmp); | ||
const result = shell.ln('-s', 'external/node_script.js', './'); | ||
t.is(result.code, 0); | ||
t.falsy(result.stderr); | ||
t.falsy(shell.error()); | ||
t.truthy(fs.existsSync('node_script.js')); | ||
t.is( | ||
fs.readFileSync('external/node_script.js').toString(), | ||
fs.readFileSync('node_script.js').toString() | ||
); | ||
}); | ||
|
||
test('Link to dead source links', t => { | ||
shell.cd(t.context.tmp); | ||
const result = shell.ln('-s', 'badlink', 'link-to-dead-link'); | ||
t.is(result.code, 0); | ||
t.falsy(result.stderr); | ||
t.falsy(shell.error()); | ||
fs.lstatSync('link-to-dead-link'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we turn this into a real assertion? This would be a good use of |
||
}); | ||
|
||
test('-f option', t => { | ||
|
@@ -161,6 +190,18 @@ test('-sf option', t => { | |
}); | ||
}); | ||
|
||
test('Override dead destination links with -sf', t => { | ||
shell.cd(t.context.tmp); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. optional: you could assert the precondition (that this link is in fact a dead link). I'd be fine with |
||
const result = shell.ln('-sf', 'file1.txt', 'badlink'); | ||
t.is(result.code, 0); | ||
t.falsy(result.stderr); | ||
t.falsy(shell.error()); | ||
t.is( | ||
fs.readFileSync('file1.txt').toString(), | ||
fs.readFileSync('badlink').toString() | ||
); | ||
}); | ||
|
||
test('Abspath regression', t => { | ||
utils.skipOnWinForEPERM(shell.ln.bind(shell, '-sf', 'file1', path.resolve(`${t.context.tmp}/abspath`)), () => { | ||
t.truthy(fs.existsSync(`${t.context.tmp}/abspath`)); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we move this to
common.isBrokenLink(nameOfPossibleLink)
? This is something I wanted for #835 anyway (and it makes more sense incommon
thanutils
). Some ideas for the behavior:WDYT?