Skip to content

Commit

Permalink
ensure symlink & link
Browse files Browse the repository at this point in the history
  • Loading branch information
reggi committed Aug 4, 2015
1 parent fc3b691 commit bb81d65
Show file tree
Hide file tree
Showing 12 changed files with 1,057 additions and 50 deletions.
55 changes: 45 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ Methods
- [ensureFileSync](#ensurefilefile-callback)
- [ensureDir](#ensuredirdir-callback)
- [ensureDirSync](#ensuredirdir-callback)
- [ensureLink](#ensurelinksrcpath-dstpath-callback)
- [ensureLinkSync](#ensurelinksrcpath-dstpath-callback)
- [ensureSymlink](#ensuresymlinksrcpath-dstpath-type-callback)
- [ensureSymlinkSync](#ensuresymlinksrcpath-dstpath-type-callback)
- [mkdirs](#mkdirsdir-callback)
- [mkdirsSync](#mkdirsdir-callback)
- [move](#movesrc-dest-options-callback)
Expand Down Expand Up @@ -137,7 +141,6 @@ word `output` to denote that if the containing directory does not exist, it shou
better succinct nomenclature for these methods, please open an issue for discussion. Thanks.



### emptyDir(dir, [callback])

Ensures that a directory is empty. If the directory does not exist, it is created. The directory itself is not deleted.
Expand All @@ -158,7 +161,6 @@ fs.emptyDir('/tmp/some/dir', function (err) {
```



### ensureFile(file, callback)

Ensures that the file exists. If the file that is requested to be created is in directories that do not exist, these directories are created. If the file already exists, it is **NOT MODIFIED**.
Expand Down Expand Up @@ -201,6 +203,47 @@ fs.ensureDir(dir, function (err) {
```


### ensureLink(srcpath, dstpath, callback)

Ensures that the link exists. If the directory structure does not exist, it is created.

Sync: `ensureLinkSync()`


Example:

```js
var fs = require('fs-extra')

var srcpath = '/tmp/file.txt'
var dstpath = '/tmp/this/path/does/not/exist/file.txt'
fs.ensureLink(srcpath, dstpath, function (err) {
console.log(err) // => null
// link has now been created, including the directory it is to be placed in
})
```


### ensureSymlink(srcpath, dstpath, [type], callback)

Ensures that the symlink exists. If the directory structure does not exist, it is created.

Sync: `ensureSymlinkSync()`


Example:

```js
var fs = require('fs-extra')

var srcpath = '/tmp/file.txt'
var dstpath = '/tmp/this/path/does/not/exist/file.txt'
fs.ensureSymlink(srcpath, dstpath, function (err) {
console.log(err) // => null
// symlink has now been created, including the directory it is to be placed in
})
```


### mkdirs(dir, callback)

Expand Down Expand Up @@ -473,11 +516,3 @@ Copyright (c) 2011-2015 [JP Richardson](https://github.com/jprichardson)


[jsonfile]: https://github.com/jprichardson/node-jsonfile








166 changes: 166 additions & 0 deletions lib/ensure/__tests__/link.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
var util = require('util')
var path = require('path')
var chai = require('chai')
var os = require('os')
var fs = require('graceful-fs')
var expect = chai.expect
var CWD = process.cwd()
var fse = require(CWD)
var ensureLink = fse.ensureLink
var ensureLinkSync = fse.ensureLinkSync

/* global afterEach, beforeEach, describe, it, after, before */

var TEST_DIR

describe('fse-ensure-link', function () {
TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'ensure-symlink')

var tests = [
// [[srcpath, dstpath], fs.link expect, ensureLink expect]
[['./foo.txt', './link.txt'], 'file-success', 'file-success'],
[['./foo.txt', './dir-foo/link.txt'], 'file-success', 'file-success'],
[['./foo.txt', './empty-dir/link.txt'], 'file-success', 'file-success'],
[['./foo.txt', './real-alpha/link.txt'], 'file-success', 'file-success'],
[['./foo.txt', './real-alpha/real-beta/link.txt'], 'file-success', 'file-success'],
[['./foo.txt', './real-alpha/real-beta/real-gamma/link.txt'], 'file-success', 'file-success'],
[['./foo.txt', './alpha/link.txt'], 'file-error', 'file-success'],
[['./foo.txt', './alpha/beta/link.txt'], 'file-error', 'file-success'],
[['./foo.txt', './alpha/beta/gamma/link.txt'], 'file-error', 'file-success'],
[['./missing.txt', './link.txt'], 'file-error', 'file-error'],
[[path.resolve(path.join(TEST_DIR, './foo.txt')), './link.txt'], 'file-success', 'file-success'],
[[path.resolve(path.join(TEST_DIR, './dir-foo/foo.txt')), './link.txt'], 'file-success', 'file-success'],
[[path.resolve(path.join(TEST_DIR, './missing.txt')), './link.txt'], 'file-error', 'file-error'],
[[path.resolve(path.join(TEST_DIR, '../foo.txt')), './link.txt'], 'file-error', 'file-error'],
[[path.resolve(path.join(TEST_DIR, '../dir-foo/foo.txt')), './link.txt'], 'file-error', 'file-error']
]

before(function () {
fse.emptyDirSync(TEST_DIR)
process.chdir(TEST_DIR)
})

beforeEach(function () {
fs.writeFileSync('./foo.txt', 'foo\n')
fse.mkdirsSync('empty-dir')
fse.mkdirsSync('dir-foo')
fs.writeFileSync('dir-foo/foo.txt', 'dir-foo\n')
fse.mkdirsSync('dir-bar')
fs.writeFileSync('dir-bar/bar.txt', 'dir-bar\n')
fse.mkdirsSync('real-alpha/real-beta/real-gamma')
})

afterEach(function (done) {
fse.emptyDir(TEST_DIR, done)
})

after(function () {
process.chdir(CWD)
fse.removeSync(TEST_DIR)
})

function fileSuccess (args, fn) {
var srcpath = args[0]
var dstpath = args[1]
var should = util.format('should create link file using src `%s` and dst `%s`', srcpath, dstpath)
it(should, function (done) {
var callback = function (err) {
if (err) return done(err)
var fileContent = fs.readFileSync(srcpath, 'utf8')
var dstDir = path.dirname(dstpath)
var dstBasename = path.basename(dstpath)
expect(fs.readdirSync(dstDir)).to.contain(dstBasename)
expect(fs.lstatSync(dstpath).isFile()).to.equal(true)
expect(fs.readFileSync(dstpath, 'utf8')).to.equal(fileContent)
return done()
}
args.push(callback)
return fn.apply(null, args)
})
}

function fileError (args, fn) {
var srcpath = args[0]
var dstpath = args[1]
var should = util.format('should return error when creating link file using src `%s` and dst `%s`', srcpath, dstpath)
it(should, function (done) {
var callback = function (err) {
expect(err).to.be.instanceof(Error)
return done()
}
args.push(callback)
return fn.apply(null, args)
})
}

function fileSuccessSync (args, fn) {
var srcpath = args[0]
var dstpath = args[1]
var should = util.format('should create link file using src `%s` and dst `%s`', srcpath, dstpath)
it(should, function () {
fn.apply(null, args)
var fileContent = fs.readFileSync(srcpath, 'utf8')
var dstDir = path.dirname(dstpath)
var dstBasename = path.basename(dstpath)
expect(fs.readdirSync(dstDir)).to.contain(dstBasename)
expect(fs.lstatSync(dstpath).isFile()).to.equal(true)
expect(fs.readFileSync(dstpath, 'utf8')).to.equal(fileContent)
})
}

function fileErrorSync (args, fn) {
var srcpath = args[0]
var dstpath = args[1]
var should = util.format('should throw error using` src `%s` and dst `%s`', srcpath, dstpath)
it(should, function () {
expect(function () {
return fn.apply(null, args)
}).to.throw
})
}

describe('fs.link()', function () {
var fn = fs.link
tests.forEach(function (test) {
var args = test[0].slice(0)
var nativeBehavior = test[1]
// var newBehavior = test[2]
if (nativeBehavior === 'file-success') fileSuccess(args, fn)
if (nativeBehavior === 'file-error') fileError(args, fn)
})
})

describe('ensureLink()', function () {
var fn = ensureLink
tests.forEach(function (test) {
var args = test[0].slice(0)
// var nativeBehavior = test[1]
var newBehavior = test[2]
if (newBehavior === 'file-success') fileSuccess(args, fn)
if (newBehavior === 'file-error') fileError(args, fn)
})
})

describe('fs.linkSync()', function () {
var fn = fs.linkSync
tests.forEach(function (test) {
var args = test[0].slice(0)
var nativeBehavior = test[1]
// var newBehavior = test[2]
if (nativeBehavior === 'file-success') fileSuccessSync(args, fn)
if (nativeBehavior === 'file-error') fileErrorSync(args, fn)
})
})

describe('ensureLinkSync()', function () {
var fn = ensureLinkSync
tests.forEach(function (test) {
var args = test[0].slice(0)
// var nativeBehavior = test[1]
var newBehavior = test[2]
if (newBehavior === 'file-success') fileSuccessSync(args, fn)
if (newBehavior === 'file-error') fileErrorSync(args, fn)
})
})

})
93 changes: 93 additions & 0 deletions lib/ensure/__tests__/symlink-paths.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
var assert = require('assert')
var util = require('util')
var path = require('path')
var os = require('os')
var fs = require('graceful-fs')
var CWD = process.cwd()
var fse = require(CWD)
var _symlinkPaths = require('../symlink-paths')
var symlinkPaths = _symlinkPaths.symlinkPaths
var symlinkPathsSync = _symlinkPaths.symlinkPathsSync
var TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'ensure-symlink')

/* global afterEach, beforeEach, describe, it, after, before */

describe('symlink-type', function () {

before(function () {
fse.emptyDirSync(TEST_DIR)
process.chdir(TEST_DIR)
})

beforeEach(function () {
fs.writeFileSync('./foo.txt', 'foo\n')
fse.mkdirsSync('./empty-dir')
fse.mkdirsSync('./dir-foo')
fs.writeFileSync('./dir-foo/foo.txt', 'dir-foo\n')
fse.mkdirsSync('./dir-bar')
fs.writeFileSync('./dir-bar/bar.txt', 'dir-bar\n')
fse.mkdirsSync('./real-alpha/real-beta/real-gamma')
})

afterEach(function (done) {
fse.emptyDir(TEST_DIR, done)
})

after(function () {
process.chdir(CWD)
fse.removeSync(TEST_DIR)
})

var tests = [
[['foo.txt', 'symlink.txt'], {toCwd: 'foo.txt', toDst: 'foo.txt'}], // smart && nodestyle
[['foo.txt', 'empty-dir/symlink.txt'], {toCwd: 'foo.txt', toDst: '../foo.txt'}], // smart
[['../foo.txt', 'empty-dir/symlink.txt'], {toCwd: 'foo.txt', toDst: '../foo.txt'}], // nodestyle
[['foo.txt', 'dir-bar/symlink.txt'], {toCwd: 'foo.txt', toDst: '../foo.txt'}], // smart
[['../foo.txt', 'dir-bar/symlink.txt'], {toCwd: 'foo.txt', toDst: '../foo.txt'}], // nodestyle
// this is to preserve node's symlink capability these arguments say create
// a link to 'dir-foo/foo.txt' this works because it exists this is unlike
// the previous example with 'empty-dir' because 'empty-dir/foo.txt' does not exist.
[['foo.txt', 'dir-foo/symlink.txt' ], {toCwd: 'dir-foo/foo.txt', toDst: 'foo.txt'}], // nodestyle
[['foo.txt', 'real-alpha/real-beta/real-gamma/symlink.txt' ], {toCwd: 'foo.txt', toDst: '../../../foo.txt'}]
]

// formats paths to pass on multiple operating systems
tests.forEach(function (test) {
test[0][0] = path.join(test[0][0])
test[0][1] = path.join(test[0][1])
test[1] = {
toCwd: path.join(test[1].toCwd),
toDst: path.join(test[1].toDst)
}
})

describe('symlinkPaths()', function () {
tests.forEach(function (test) {
var args = test[0].slice(0)
var expectedRelativePaths = test[1]
var should = util.format('should return \'%s\' when src \'%s\' and dst is \'%s\'', JSON.stringify(expectedRelativePaths), args[0], args[1])
it(should, function (done) {
var callback = function (err, relativePaths) {
if (err) done(err)
assert.deepEqual(relativePaths, expectedRelativePaths)
done()
}
args.push(callback)
return symlinkPaths.apply(null, args)
})
})
})

describe('symlinkPathsSync()', function () {
tests.forEach(function (test) {
var args = test[0].slice(0)
var expectedRelativePaths = test[1]
var should = util.format('should return \'%s\' when src \'%s\' and dst is \'%s\'', JSON.stringify(expectedRelativePaths), args[0], args[1])
it(should, function () {
var relativePaths = symlinkPathsSync.apply(null, args)
assert.deepEqual(relativePaths, expectedRelativePaths)
})
})
})

})
Loading

0 comments on commit bb81d65

Please sign in to comment.