[Pkg-javascript-commits] [node-fs-extra] 07/10: New upstream version 1.0.0

Julien Puydt julien.puydt at laposte.net
Sun Nov 6 08:54:55 UTC 2016


This is an automated email from the git hooks/post-receive script.

jpuydt-guest pushed a commit to branch master
in repository node-fs-extra.

commit 4789760fb72634eee7dab67ce8c47e3e08a35b09
Author: Julien Puydt <julien.puydt at laposte.net>
Date:   Sun Nov 6 09:38:20 2016 +0100

    New upstream version 1.0.0
---
 .gitignore                                         |   4 +-
 .npmignore                                         |   7 +-
 .travis.yml                                        |  22 +-
 CHANGELOG.md                                       | 475 ++++++++++++++++++++-
 LICENSE                                            |   8 +-
 README.md                                          | 392 ++++++++++++-----
 appveyor.yml                                       |  26 ++
 lib/__tests__/fs-integration.test.js               |  34 ++
 lib/copy-sync/__tests__/broken-symlink.test.js     |  59 +++
 lib/copy-sync/__tests__/copy-sync-dir.test.js      | 131 ++++++
 lib/copy-sync/__tests__/copy-sync-file.test.js     | 180 ++++++++
 .../__tests__/copy-sync-preserve-time.test.js      |  60 +++
 .../copy-sync/__tests__/fixtures}/a-file           |   0
 .../__tests__/fixtures}/a-folder/another-file      |   0
 .../fixtures}/a-folder/another-folder/file3        |   0
 lib/copy-sync/__tests__/symlink.test.js            |  76 ++++
 lib/copy-sync/copy-file-sync.js                    |  42 ++
 lib/copy-sync/copy-sync.js                         |  56 +++
 lib/copy-sync/index.js                             |   3 +
 lib/copy.js                                        |  99 -----
 lib/copy/__tests__/async/copy-gh-89.test.js        |  54 +++
 lib/copy/__tests__/copy-dev-null.test.js           |  34 ++
 lib/copy/__tests__/copy-permissions.test.js        | 108 +++++
 lib/copy/__tests__/copy-preserve-time.test.js      |  68 +++
 lib/copy/__tests__/copy.test.js                    | 193 +++++++++
 .../move => lib/copy/__tests__/fixtures}/a-file    |   0
 .../copy/__tests__/fixtures}/a-folder/another-file |   0
 .../fixtures}/a-folder/another-folder/file3        |   0
 lib/copy/__tests__/ncp/README.md                   |   1 +
 lib/copy/__tests__/ncp/broken-symlink.test.js      |  58 +++
 .../__tests__}/ncp/fixtures/modified-files/out/a   |   0
 .../__tests__}/ncp/fixtures/modified-files/src/a   |   0
 .../__tests__}/ncp/fixtures/regular-fixtures/out/a |   0
 .../__tests__}/ncp/fixtures/regular-fixtures/out/b |   0
 .../__tests__}/ncp/fixtures/regular-fixtures/out/c |   0
 .../__tests__}/ncp/fixtures/regular-fixtures/out/d |   0
 .../__tests__}/ncp/fixtures/regular-fixtures/out/e |   0
 .../__tests__}/ncp/fixtures/regular-fixtures/out/f |   0
 .../ncp/fixtures/regular-fixtures/out/sub/a        |   0
 .../ncp/fixtures/regular-fixtures/out/sub/b        |   0
 .../__tests__}/ncp/fixtures/regular-fixtures/src/a |   0
 .../__tests__}/ncp/fixtures/regular-fixtures/src/b |   0
 .../__tests__}/ncp/fixtures/regular-fixtures/src/c |   0
 .../__tests__}/ncp/fixtures/regular-fixtures/src/d |   0
 .../__tests__}/ncp/fixtures/regular-fixtures/src/e |   0
 .../__tests__}/ncp/fixtures/regular-fixtures/src/f |   0
 .../ncp/fixtures/regular-fixtures/src/sub/a        |   0
 .../ncp/fixtures/regular-fixtures/src/sub/b        |   0
 lib/copy/__tests__/ncp/ncp-error-perm.test.js      |  50 +++
 lib/copy/__tests__/ncp/ncp.test.js                 | 171 ++++++++
 lib/copy/__tests__/ncp/symlink.test.js             |  78 ++++
 lib/copy/copy.js                                   |  50 +++
 lib/copy/index.js                                  |   3 +
 lib/{_copy.js => copy/ncp.js}                      | 186 ++++----
 lib/empty/__tests__/empty-dir-sync.test.js         |  54 +++
 lib/empty/__tests__/empty-dir.test.js              |  63 +++
 lib/empty/index.js                                 |  47 ++
 {test => lib/ensure/__tests__}/create.test.js      |  39 +-
 {test => lib/ensure/__tests__}/ensure.test.js      |  66 +--
 lib/ensure/__tests__/link.test.js                  | 223 ++++++++++
 lib/ensure/__tests__/symlink-paths.test.js         |  91 ++++
 lib/ensure/__tests__/symlink-type.test.js          | 122 ++++++
 lib/ensure/__tests__/symlink.test.js               | 450 +++++++++++++++++++
 lib/{create.js => ensure/file.js}                  |  20 +-
 lib/ensure/index.js                                |  21 +
 lib/ensure/link.js                                 |  58 +++
 lib/ensure/symlink-paths.js                        |  96 +++++
 lib/ensure/symlink-type.js                         |  27 ++
 lib/ensure/symlink.js                              |  62 +++
 lib/index.js                                       | 108 ++---
 lib/json.js                                        |  31 --
 lib/json/__tests__/jsonfile-integration.test.js    |  39 ++
 lib/json/__tests__/output-json-sync.test.js        |  48 +++
 lib/json/__tests__/output-json.test.js             |  53 +++
 {test => lib/json/__tests__}/read.test.js          |  25 +-
 lib/json/__tests__/spaces.test.js                  |  34 ++
 lib/json/index.js                                  |   9 +
 lib/json/jsonfile.js                               |  14 +
 lib/json/output-json-sync.js                       |  16 +
 lib/json/output-json.js                            |  24 ++
 lib/mkdir.js                                       | 103 -----
 lib/mkdirs/__tests__/chmod.test.js                 |  67 +++
 lib/mkdirs/__tests__/clobber.test.js               |  53 +++
 lib/mkdirs/__tests__/issue-209.test.js             |  38 ++
 lib/mkdirs/__tests__/issue-93.test.js              |  36 ++
 {test => lib/mkdirs/__tests__}/mkdir.test.js       |  31 +-
 lib/mkdirs/__tests__/mkdirp.test.js                |  51 +++
 lib/mkdirs/__tests__/opts-undef.test.js            |  29 ++
 lib/mkdirs/__tests__/perm.test.js                  |  54 +++
 lib/mkdirs/__tests__/perm_sync.test.js             |  58 +++
 lib/mkdirs/__tests__/race.test.js                  |  68 +++
 lib/mkdirs/__tests__/rel.test.js                   |  61 +++
 lib/mkdirs/__tests__/return.test.js                |  41 ++
 lib/mkdirs/__tests__/return_sync.test.js           |  39 ++
 lib/mkdirs/__tests__/root.test.js                  |  27 ++
 lib/mkdirs/__tests__/sync.test.js                  |  58 +++
 lib/mkdirs/__tests__/umask.test.js                 |  80 ++++
 lib/mkdirs/index.js                                |   9 +
 lib/mkdirs/mkdirs-sync.js                          |  57 +++
 lib/mkdirs/mkdirs.js                               |  61 +++
 lib/mkdirs/win32.js                                |  24 ++
 .../move => lib/move/__tests__/fixtures}/a-file    |   0
 .../move/__tests__/fixtures}/a-folder/another-file |   0
 .../fixtures}/a-folder/another-folder/file3        |   0
 lib/move/__tests__/move-clobber.test.js            |  68 +++
 lib/move/__tests__/move.test.js                    | 285 +++++++++++++
 lib/{move.js => move/index.js}                     |  91 ++--
 {test => lib/output/__tests__}/output.test.js      |  39 +-
 lib/{output.js => output/index.js}                 |  13 +-
 lib/remove.js                                      |  14 -
 lib/remove/__tests__/remove-dir.test.js            |  27 ++
 lib/remove/__tests__/remove-file.test.js           |   1 +
 lib/remove/__tests__/remove-sync-dir.test.js       |  31 ++
 lib/remove/__tests__/remove-sync-file.test.js      |  26 ++
 lib/remove/__tests__/remove.test.js                |  84 ++++
 lib/remove/index.js                                |  15 +
 lib/remove/rimraf.js                               | 301 +++++++++++++
 lib/util/__tests__/utimes.test.js                  | 122 ++++++
 lib/util/assign.js                                 |  14 +
 lib/util/utimes.js                                 |  70 +++
 .../walk-sync/__tests__/fixtures/dir1/file1_2      |   0
 .../__tests__/fixtures/dir2/dir2_1/file2_1_1       |   0
 .../c => lib/walk-sync/__tests__/fixtures/file1    |   0
 lib/walk-sync/__tests__/walkSync.test.js           |  41 ++
 lib/walk-sync/index.js                             |  20 +
 lib/walk/__tests__/fixtures.json                   |   7 +
 lib/walk/__tests__/walk.test.js                    |  73 ++++
 lib/walk/index.js                                  |   5 +
 package.json                                       |  32 +-
 test.js                                            |  30 ++
 test/copy-sync.test.js                             | 223 ----------
 test/copy.test.js                                  | 238 -----------
 test/fs-integration.test.js                        |  23 -
 test/json.test.js                                  |  50 ---
 test/lib/util.js                                   |  36 --
 test/mkdirp/README.md                              |   1 -
 test/mkdirp/chmod.test.js                          |  52 ---
 test/mkdirp/clobber.test.js                        |  41 --
 test/mkdirp/mkdirp.test.js                         |  34 --
 test/mkdirp/opts_fs.test.js                        |  32 --
 test/mkdirp/opts_fs_sync.test.js                   |  30 --
 test/mkdirp/perm.test.js                           |  41 --
 test/mkdirp/perm_sync.js                           |  46 --
 test/mkdirp/race.test.js                           |  42 --
 test/mkdirp/rel.test.js                            |  32 --
 test/mkdirp/return.test.js                         |  29 --
 test/mkdirp/return_sync.test.js                    |  27 --
 test/mkdirp/root.test.js                           |  22 -
 test/mkdirp/sync.test.js                           |  31 --
 test/mkdirp/umask.test.js                          |  75 ----
 test/mocha.opts                                    |   5 -
 test/move.test.js                                  | 262 ------------
 .../broken-symlink-fixtures/src/broken-symlink     |   1 -
 test/ncp/fixtures/regular-fixtures/out/sub/z       |   1 -
 test/ncp/fixtures/regular-fixtures/out/z           |   1 -
 .../fixtures/symlink-fixtures/out/dir-symlink/bar  |   1 -
 test/ncp/fixtures/symlink-fixtures/out/dir/bar     |   1 -
 .../ncp/fixtures/symlink-fixtures/out/file-symlink |   1 -
 test/ncp/fixtures/symlink-fixtures/out/foo         |   1 -
 test/ncp/fixtures/symlink-fixtures/src/dir-symlink |   1 -
 test/ncp/fixtures/symlink-fixtures/src/dir/bar     |   1 -
 .../ncp/fixtures/symlink-fixtures/src/file-symlink |   1 -
 test/ncp/fixtures/symlink-fixtures/src/foo         |   1 -
 test/ncp/ncp.test.js                               | 198 ---------
 test/readme.md                                     |   1 +
 test/remove.test.js                                | 124 ------
 166 files changed, 6318 insertions(+), 2410 deletions(-)

diff --git a/.gitignore b/.gitignore
index 5281cfd..68b876c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
+.nyc_output/
 coverage/
 node_modules/
 
 .idea
-*.iml
\ No newline at end of file
+*.iml
+npm-debug.log
diff --git a/.npmignore b/.npmignore
index 950be15..68eefb7 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,3 +1,8 @@
+.nyc_output/
 coverage/
 test/
-.travis.yml
\ No newline at end of file
+.travis.yml
+appveyor.yml
+lib/**/__tests__/
+test/readme.md
+test.js
diff --git a/.travis.yml b/.travis.yml
index 22a70cd..2cc138c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,10 +1,18 @@
+sudo: false
 language: node_js
-before_install:
-  - "npm install npm -g"
 node_js:
-  - 0.11
-  - 0.10
+  - "0.12"
+  - "io.js"
+  - "4"
+  - "5"
+  - "6"
+  - "7"
+matrix:
+  include:
+    - node_js: "7"
+      env: TEST_SUITE=lint
 env:
-  - TEST_SUITE=test
-  - TEST_SUITE=coveralls
-script: "npm run-script $TEST_SUITE"
+  - TEST_SUITE=unit
+script: npm run-script $TEST_SUITE
+after_success:
+  - if [ $TEST_SUITE = lint ]; then npm run coveralls; fi
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c964faa..43b1f3a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,163 @@
+Unreleased
+----------
+
+### Added
+
+### Removed
+
+### Changed
+
+### Fixed
+
+
+1.0.0 / 2016-11-01
+------------------
+
+After five years of development, we finally have reach the 1.0.0 milestone! Big thanks goes
+to [Ryan Zim](https://github.com/RyanZim) for leading the charge on this release!
+
+### Added
+- `walkSync()`
+
+### Changed
+- **BREAKING**: dropped Node v0.10 support.
+- disabled `rimaf` globbing, wasn't used. [#280]
+- deprecate `copy()/copySync()` option `filter` if it's a `RegExp`. `filter` should now be a function.
+- inline `rimraf`. This is temporary and was done because `rimraf` depended upon the beefy `glob` which `fs-extra` does not use. [#300]
+
+### Fixed
+- bug fix proper closing of file handle on `utimesMillis()` [#271]
+- proper escaping of files with dollar signs [#291]
+- `copySync()` failed if user didn't own file. [#199], [#301]
+
+
+0.30.0 / 2016-04-28
+-------------------
+- Brought back Node v0.10 support. I didn't realize there was still demand. Official support will end **2016-10-01**.
+
+0.29.0 / 2016-04-27
+-------------------
+- **BREAKING**: removed support for Node v0.10. If you still want to use Node v0.10, everything should work except for `ensureLink()/ensureSymlink()`. Node v0.12 is still supported but will be dropped in the near future as well.
+
+0.28.0 / 2016-04-17
+-------------------
+- **BREAKING**: removed `createOutputStream()`. Use https://www.npmjs.com/package/create-output-stream. See: [#192][#192]
+- `mkdirs()/mkdirsSync()` check for invalid win32 path chars. See: [#209][#209], [#237][#237]
+- `mkdirs()/mkdirsSync()` if drive not mounted, error. See: [#93][#93]
+
+0.27.0 / 2016-04-15
+-------------------
+- add `dereference` option to `copySync()`. [#235][#235]
+
+0.26.7 / 2016-03-16
+-------------------
+- fixed `copy()` if source and dest are the same. [#230][#230]
+
+0.26.6 / 2016-03-15
+-------------------
+- fixed if `emptyDir()` does not have a callback: [#229][#229]
+
+0.26.5 / 2016-01-27
+-------------------
+- `copy()` with two arguments (w/o callback) was broken. See: [#215][#215]
+
+0.26.4 / 2016-01-05
+-------------------
+- `copySync()` made `preserveTimestamps` default consistent with `copy()` which is `false`. See: [#208][#208]
+
+0.26.3 / 2015-12-17
+-------------------
+- fixed `copy()` hangup in copying blockDevice / characterDevice / `/dev/null`. See: [#193][#193]
+
+0.26.2 / 2015-11-02
+-------------------
+- fixed `outputJson{Sync}()` spacing adherence to `fs.spaces`
+
+0.26.1 / 2015-11-02
+-------------------
+- fixed `copySync()` when `clogger=true` and the destination is read only. See: [#190][#190]
+
+0.26.0 / 2015-10-25
+-------------------
+- extracted the `walk()` function into its own module [`klaw`](https://github.com/jprichardson/node-klaw).
+
+0.25.0 / 2015-10-24
+-------------------
+- now has a file walker `walk()`
+
+0.24.0 / 2015-08-28
+-------------------
+- removed alias `delete()` and `deleteSync()`. See: [#171][#171]
+
+0.23.1 / 2015-08-07
+-------------------
+- Better handling of errors for `move()` when moving across devices. [#170][#170]
+- `ensureSymlink()` and `ensureLink()` should not throw errors if link exists. [#169][#169]
+
+0.23.0 / 2015-08-06
+-------------------
+- added `ensureLink{Sync}()` and `ensureSymlink{Sync}()`. See: [#165][#165]
+
+0.22.1 / 2015-07-09
+-------------------
+- Prevent calling `hasMillisResSync()` on module load. See: [#149][#149].
+Fixes regression that was introduced in `0.21.0`.
+
+0.22.0 / 2015-07-09
+-------------------
+- preserve permissions / ownership in `copy()`. See: [#54][#54]
+
+0.21.0 / 2015-07-04
+-------------------
+- add option to preserve timestamps in `copy()` and `copySync()`. See: [#141][#141]
+- updated `graceful-fs at 3.x` to `4.x`. This brings in features from `amazing-graceful-fs` (much cleaner code / less hacks)
+
+0.20.1 / 2015-06-23
+-------------------
+- fixed regression caused by latest jsonfile update: See: https://github.com/jprichardson/node-jsonfile/issues/26
+
+0.20.0 / 2015-06-19
+-------------------
+- removed `jsonfile` aliases with `File` in the name, they weren't documented and probably weren't in use e.g.
+this package had both `fs.readJsonFile` and `fs.readJson` that were aliases to each other, now use `fs.readJson`.
+- preliminary walker created. Intentionally not documented. If you use it, it will almost certainly change and break your code.
+- started moving tests inline
+- upgraded to `jsonfile at 2.1.0`, can now pass JSON revivers/replacers to `readJson()`, `writeJson()`, `outputJson()`
+
+0.19.0 / 2015-06-08
+-------------------
+- `fs.copy()` had support for Node v0.8, dropped support
+
+0.18.4 / 2015-05-22
+-------------------
+- fixed license field according to this: [#136][#136] and https://github.com/npm/npm/releases/tag/v2.10.0
+
+0.18.3 / 2015-05-08
+-------------------
+- bugfix: handle `EEXIST` when clobbering on some Linux systems. [#134][#134]
+
+0.18.2 / 2015-04-17
+-------------------
+- bugfix: allow `F_OK` ([#120][#120])
+
+0.18.1 / 2015-04-15
+-------------------
+- improved windows support for `move()` a bit. https://github.com/jprichardson/node-fs-extra/commit/92838980f25dc2ee4ec46b43ee14d3c4a1d30c1b
+- fixed a lot of tests for Windows (appveyor)
+
+0.18.0 / 2015-03-31
+-------------------
+- added `emptyDir()` and `emptyDirSync()`
+
+0.17.0 / 2015-03-28
+-------------------
+- `copySync` added `clobber` option (before always would clobber, now if `clobber` is `false` it throws an error if the destination exists).
+**Only works with files at the moment.**
+- `createOutputStream()` added. See: [#118][#118]
+
 0.16.5 / 2015-03-08
 -------------------
-- fixed `fs.move` when `clobber` is `true` and destination is a directory, it should clobber. https://github.com/jprichardson/node-fs-extra/issues/114
+- fixed `fs.move` when `clobber` is `true` and destination is a directory, it should clobber. [#114][#114]
 
 0.16.4 / 2015-03-01
 -------------------
@@ -35,22 +192,22 @@
 
 0.14.0 / 2015-01-05
 -------------------
-- changed `copy`/`copySync` from `fs.copy(src, dest, [filters], callback)` to `fs.copy(src, dest, [options], callback)` https://github.com/jprichardson/node-fs-extra/pull/100
+- changed `copy`/`copySync` from `fs.copy(src, dest, [filters], callback)` to `fs.copy(src, dest, [options], callback)` [#100][#100]
 - removed mockfs tests for mkdirp (this may be temporary, but was getting in the way of other tests)
 
 0.13.0 / 2014-12-10
 -------------------
 - removed `touch` and `touchSync` methods (they didn't handle permissions like UNIX touch)
 - updated `"ncp": "^0.6.0"` to `"ncp": "^1.0.1"`
-- imported `mkdirp` => `minimist` and `mkdirp` are no longer dependences, should now appease people who wanted `mkdirp` to be `--use_strict` safe. See [#59](https://github.com/jprichardson/node-fs-extra/issues/59)
+- imported `mkdirp` => `minimist` and `mkdirp` are no longer dependences, should now appease people who wanted `mkdirp` to be `--use_strict` safe. See [#59]([#59][#59])
 
 0.12.0 / 2014-09-22
 -------------------
-- copy symlinks in `copySync()` [#85](https://github.com/jprichardson/node-fs-extra/pull/85)
+- copy symlinks in `copySync()` [#85][#85]
 
 0.11.1 / 2014-09-02
 -------------------
-- bugfix `copySync()` preserve file permissions [#80](https://github.com/jprichardson/node-fs-extra/pull/80)
+- bugfix `copySync()` preserve file permissions [#80][#80]
 
 0.11.0 / 2014-08-11
 -------------------
@@ -189,3 +346,311 @@ from `~` to `^`. #67
 ------------------
 * Added methods rmrf and rmrfSync
 * Moved tests from Jasmine to Mocha
+
+
+[#307]: https://github.com/jprichardson/node-fs-extra/pull/307      "Fix coverage"
+[#306]: https://github.com/jprichardson/node-fs-extra/pull/306      "Update devDeps, fix lint error"
+[#305]: https://github.com/jprichardson/node-fs-extra/pull/305      "Re-add Coveralls"
+[#304]: https://github.com/jprichardson/node-fs-extra/pull/304      "Remove path-is-absolute [enhancement]"
+[#303]: https://github.com/jprichardson/node-fs-extra/pull/303      "Document copySync filter inconsistency [documentation, feature-copy]"
+[#302]: https://github.com/jprichardson/node-fs-extra/pull/302      "fix(console): depreciated -> deprecated"
+[#301]: https://github.com/jprichardson/node-fs-extra/pull/301      "Remove chmod call from copySync [feature-copy]"
+[#300]: https://github.com/jprichardson/node-fs-extra/pull/300      "Inline Rimraf [enhancement, feature-move, feature-remove]"
+[#299]: https://github.com/jprichardson/node-fs-extra/pull/299      "Warn when filter is a RegExp [feature-copy]"
+[#298]: https://github.com/jprichardson/node-fs-extra/issues/298    "API Docs [documentation]"
+[#297]: https://github.com/jprichardson/node-fs-extra/pull/297      "Warn about using preserveTimestamps on 32-bit node"
+[#296]: https://github.com/jprichardson/node-fs-extra/pull/296      "Improve EEXIST error message for copySync [enhancement]"
+[#295]: https://github.com/jprichardson/node-fs-extra/pull/295      "Depreciate using regular expressions for copy's filter option [documentation]"
+[#294]: https://github.com/jprichardson/node-fs-extra/pull/294      "BREAKING: Refactor lib/copy/ncp.js [feature-copy]"
+[#293]: https://github.com/jprichardson/node-fs-extra/pull/293      "Update CI configs"
+[#292]: https://github.com/jprichardson/node-fs-extra/issues/292    "Rewrite lib/copy/ncp.js [enhancement, feature-copy]"
+[#291]: https://github.com/jprichardson/node-fs-extra/pull/291      "Escape '$' in replacement string for async file copying"
+[#290]: https://github.com/jprichardson/node-fs-extra/issues/290    "Exclude files pattern while copying using copy.config.js [question]"
+[#289]: https://github.com/jprichardson/node-fs-extra/pull/289      "(Closes #271) lib/util/utimes: properly close file descriptors in the event of an error"
+[#288]: https://github.com/jprichardson/node-fs-extra/pull/288      "(Closes #271) lib/util/utimes: properly close file descriptors in the event of an error"
+[#287]: https://github.com/jprichardson/node-fs-extra/issues/287    "emptyDir() callback arguments are inconsistent [enhancement, feature-remove]"
+[#286]: https://github.com/jprichardson/node-fs-extra/pull/286      "Added walkSync function"
+[#285]: https://github.com/jprichardson/node-fs-extra/issues/285    "CITGM test failing on s390"
+[#284]: https://github.com/jprichardson/node-fs-extra/issues/284    "outputFile method is missing a check to determine if existing item is a folder or not"
+[#283]: https://github.com/jprichardson/node-fs-extra/pull/283      "Apply filter also on directories and symlinks for copySync()"
+[#282]: https://github.com/jprichardson/node-fs-extra/pull/282      "Apply filter also on directories and symlinks for copySync()"
+[#281]: https://github.com/jprichardson/node-fs-extra/issues/281    "remove function executes "successfully" but doesn't do anything?"
+[#280]: https://github.com/jprichardson/node-fs-extra/pull/280      "Disable rimraf globbing"
+[#279]: https://github.com/jprichardson/node-fs-extra/issues/279    "Some code is vendored instead of included [awaiting-reply]"
+[#278]: https://github.com/jprichardson/node-fs-extra/issues/278    "copy() does not preserve file/directory ownership"
+[#277]: https://github.com/jprichardson/node-fs-extra/pull/277      "Mention defaults for clobber and dereference options"
+[#276]: https://github.com/jprichardson/node-fs-extra/issues/276    "Cannot connect to Shared Folder [awaiting-reply]"
+[#275]: https://github.com/jprichardson/node-fs-extra/issues/275    "EMFILE, too many open files on Mac OS with JSON API"
+[#274]: https://github.com/jprichardson/node-fs-extra/issues/274    "Use with memory-fs? [enhancement, future]"
+[#273]: https://github.com/jprichardson/node-fs-extra/pull/273      "tests: rename `remote.test.js` to `remove.test.js`"
+[#272]: https://github.com/jprichardson/node-fs-extra/issues/272    "Copy clobber flag never err even when true [bug, feature-copy]"
+[#271]: https://github.com/jprichardson/node-fs-extra/issues/271    "Unclosed file handle on futimes error"
+[#270]: https://github.com/jprichardson/node-fs-extra/issues/270    "copy not working as desired on Windows [feature-copy, platform-windows]"
+[#269]: https://github.com/jprichardson/node-fs-extra/issues/269    "Copying with preserveTimeStamps: true is inaccurate using 32bit node [feature-copy]"
+[#268]: https://github.com/jprichardson/node-fs-extra/pull/268      "port fix for mkdirp issue #111"
+[#267]: https://github.com/jprichardson/node-fs-extra/issues/267    "WARN deprecated wrench at 1.5.9: wrench.js is deprecated!"
+[#266]: https://github.com/jprichardson/node-fs-extra/issues/266    "fs-extra"
+[#265]: https://github.com/jprichardson/node-fs-extra/issues/265    "Link the `fs.stat fs.exists` etc. methods for replace the `fs` module forever?"
+[#264]: https://github.com/jprichardson/node-fs-extra/issues/264    "Renaming a file using move fails when a file inside is open (at least on windows) [wont-fix]"
+[#263]: https://github.com/jprichardson/node-fs-extra/issues/263    "ENOSYS: function not implemented, link [needs-confirmed]"
+[#262]: https://github.com/jprichardson/node-fs-extra/issues/262    "Add .exists() and .existsSync()"
+[#261]: https://github.com/jprichardson/node-fs-extra/issues/261    "Cannot read property 'prototype' of undefined"
+[#260]: https://github.com/jprichardson/node-fs-extra/pull/260      "use more specific path for method require"
+[#259]: https://github.com/jprichardson/node-fs-extra/issues/259    "Feature Request: isEmpty"
+[#258]: https://github.com/jprichardson/node-fs-extra/issues/258    "copy files does not preserve file timestamp"
+[#257]: https://github.com/jprichardson/node-fs-extra/issues/257    "Copying a file on windows fails"
+[#256]: https://github.com/jprichardson/node-fs-extra/pull/256      "Updated Readme "
+[#255]: https://github.com/jprichardson/node-fs-extra/issues/255    "Update rimraf required version"
+[#254]: https://github.com/jprichardson/node-fs-extra/issues/254    "request for readTree, readTreeSync, walkSync method"
+[#253]: https://github.com/jprichardson/node-fs-extra/issues/253    "outputFile does not touch mtime when file exists"
+[#252]: https://github.com/jprichardson/node-fs-extra/pull/252      "Fixing problem when copying file with no write permission"
+[#251]: https://github.com/jprichardson/node-fs-extra/issues/251    "Just wanted to say thank you"
+[#250]: https://github.com/jprichardson/node-fs-extra/issues/250    "`fs.remove()` not removing files (works with `rm -rf`)"
+[#249]: https://github.com/jprichardson/node-fs-extra/issues/249    "Just a Question ... Remove Servers"
+[#248]: https://github.com/jprichardson/node-fs-extra/issues/248    "Allow option to not preserve permissions for copy"
+[#247]: https://github.com/jprichardson/node-fs-extra/issues/247    "Add TypeScript typing directly in the fs-extra package"
+[#246]: https://github.com/jprichardson/node-fs-extra/issues/246    "fse.remove() && fse.removeSync() don't throw error on ENOENT file"
+[#245]: https://github.com/jprichardson/node-fs-extra/issues/245    "filter for empty dir [enhancement]"
+[#244]: https://github.com/jprichardson/node-fs-extra/issues/244    "copySync doesn't apply the filter to directories"
+[#243]: https://github.com/jprichardson/node-fs-extra/issues/243    "Can I request fs.walk() to be synchronous?"
+[#242]: https://github.com/jprichardson/node-fs-extra/issues/242    "Accidentally truncates file names ending with $$ [bug, feature-copy]"
+[#241]: https://github.com/jprichardson/node-fs-extra/pull/241      "Remove link to createOutputStream"
+[#240]: https://github.com/jprichardson/node-fs-extra/issues/240    "walkSync request"
+[#239]: https://github.com/jprichardson/node-fs-extra/issues/239    "Depreciate regular expressions for copy's filter [documentation, feature-copy]"
+[#238]: https://github.com/jprichardson/node-fs-extra/issues/238    "Can't write to files while in a worker thread."
+[#237]: https://github.com/jprichardson/node-fs-extra/issues/237    ".ensureDir(..) fails silently when passed an invalid path..."
+[#236]: https://github.com/jprichardson/node-fs-extra/issues/236    "[Removed] Filed under wrong repo"
+[#235]: https://github.com/jprichardson/node-fs-extra/pull/235      "Adds symlink dereference option to `fse.copySync` (#191)"
+[#234]: https://github.com/jprichardson/node-fs-extra/issues/234    "ensureDirSync fails silent when EACCES: permission denied on travis-ci"
+[#233]: https://github.com/jprichardson/node-fs-extra/issues/233    "please make sure the first argument in callback is error object [feature-copy]"
+[#232]: https://github.com/jprichardson/node-fs-extra/issues/232    "Copy a folder content  to its child folder.  "
+[#231]: https://github.com/jprichardson/node-fs-extra/issues/231    "Adding read/write/output functions for YAML"
+[#230]: https://github.com/jprichardson/node-fs-extra/pull/230      "throw error if src and dest are the same to avoid zeroing out + test"
+[#229]: https://github.com/jprichardson/node-fs-extra/pull/229      "fix 'TypeError: callback is not a function' in emptyDir"
+[#228]: https://github.com/jprichardson/node-fs-extra/pull/228      "Throw error when target is empty so file is not accidentally zeroed out"
+[#227]: https://github.com/jprichardson/node-fs-extra/issues/227    "Uncatchable errors when there are invalid arguments [feature-move]"
+[#226]: https://github.com/jprichardson/node-fs-extra/issues/226    "Moving to the current directory"
+[#225]: https://github.com/jprichardson/node-fs-extra/issues/225    "EBUSY: resource busy or locked, unlink"
+[#224]: https://github.com/jprichardson/node-fs-extra/issues/224    "fse.copy ENOENT error"
+[#223]: https://github.com/jprichardson/node-fs-extra/issues/223    "Suspicious behavior of fs.existsSync"
+[#222]: https://github.com/jprichardson/node-fs-extra/pull/222      "A clearer description of emtpyDir function"
+[#221]: https://github.com/jprichardson/node-fs-extra/pull/221      "Update README.md"
+[#220]: https://github.com/jprichardson/node-fs-extra/pull/220      "Non-breaking feature: add option 'passStats' to copy methods."
+[#219]: https://github.com/jprichardson/node-fs-extra/pull/219      "Add closing parenthesis in copySync example"
+[#218]: https://github.com/jprichardson/node-fs-extra/pull/218      "fix #187 #70 options.filter bug"
+[#217]: https://github.com/jprichardson/node-fs-extra/pull/217      "fix #187 #70 options.filter bug"
+[#216]: https://github.com/jprichardson/node-fs-extra/pull/216      "fix #187 #70 options.filter bug"
+[#215]: https://github.com/jprichardson/node-fs-extra/pull/215      "fse.copy throws error when only src and dest provided [bug, documentation, feature-copy]"
+[#214]: https://github.com/jprichardson/node-fs-extra/pull/214      "Fixing copySync anchor tag"
+[#213]: https://github.com/jprichardson/node-fs-extra/issues/213    "Merge extfs with this repo"
+[#212]: https://github.com/jprichardson/node-fs-extra/pull/212      "Update year to 2016 in README.md and LICENSE"
+[#211]: https://github.com/jprichardson/node-fs-extra/issues/211    "Not copying all files"
+[#210]: https://github.com/jprichardson/node-fs-extra/issues/210    "copy/copySync behave differently when copying a symbolic file [bug, documentation, feature-copy]"
+[#209]: https://github.com/jprichardson/node-fs-extra/issues/209    "In Windows invalid directory name causes infinite loop in ensureDir(). [bug]"
+[#208]: https://github.com/jprichardson/node-fs-extra/pull/208      "fix options.preserveTimestamps to false in copy-sync by default [feature-copy]"
+[#207]: https://github.com/jprichardson/node-fs-extra/issues/207    "Add `compare` suite of functions"
+[#206]: https://github.com/jprichardson/node-fs-extra/issues/206    "outputFileSync"
+[#205]: https://github.com/jprichardson/node-fs-extra/issues/205    "fix documents about copy/copySync [documentation, feature-copy]"
+[#204]: https://github.com/jprichardson/node-fs-extra/pull/204      "allow copy of block and character device files"
+[#203]: https://github.com/jprichardson/node-fs-extra/issues/203    "copy method's argument options couldn't be undefined [bug, feature-copy]"
+[#202]: https://github.com/jprichardson/node-fs-extra/issues/202    "why there is not a walkSync method?"
+[#201]: https://github.com/jprichardson/node-fs-extra/issues/201    "clobber for directories [feature-copy, future]"
+[#200]: https://github.com/jprichardson/node-fs-extra/issues/200    "'copySync' doesn't work in sync"
+[#199]: https://github.com/jprichardson/node-fs-extra/issues/199    "fs.copySync fails if user does not own file [bug, feature-copy]"
+[#198]: https://github.com/jprichardson/node-fs-extra/issues/198    "handle copying between identical files [feature-copy]"
+[#197]: https://github.com/jprichardson/node-fs-extra/issues/197    "Missing documentation for `outputFile` `options` 3rd parameter [documentation]"
+[#196]: https://github.com/jprichardson/node-fs-extra/issues/196    "copy filter: async function and/or function called with `fs.stat` result [future]"
+[#195]: https://github.com/jprichardson/node-fs-extra/issues/195    "How to override with outputFile?"
+[#194]: https://github.com/jprichardson/node-fs-extra/pull/194      "allow ensureFile(Sync) to provide data to be written to created file"
+[#193]: https://github.com/jprichardson/node-fs-extra/issues/193    "`fs.copy` fails silently if source file is /dev/null [bug, feature-copy]"
+[#192]: https://github.com/jprichardson/node-fs-extra/issues/192    "Remove fs.createOutputStream()"
+[#191]: https://github.com/jprichardson/node-fs-extra/issues/191    "How to copy symlinks to target as normal folders [feature-copy]"
+[#190]: https://github.com/jprichardson/node-fs-extra/pull/190      "copySync to overwrite destination file if readonly and clobber true"
+[#189]: https://github.com/jprichardson/node-fs-extra/pull/189      "move.test fix to support CRLF on Windows"
+[#188]: https://github.com/jprichardson/node-fs-extra/issues/188    "move.test failing on windows platform"
+[#187]: https://github.com/jprichardson/node-fs-extra/issues/187    "Not filter each file, stops on first false [feature-copy]"
+[#186]: https://github.com/jprichardson/node-fs-extra/issues/186    "Do you need a .size() function in this module? [future]"
+[#185]: https://github.com/jprichardson/node-fs-extra/issues/185    "Doesn't work on NodeJS v4.x"
+[#184]: https://github.com/jprichardson/node-fs-extra/issues/184    "CLI equivalent for fs-extra"
+[#183]: https://github.com/jprichardson/node-fs-extra/issues/183    "with clobber true, copy and copySync behave differently if destination file is read only [bug, feature-copy]"
+[#182]: https://github.com/jprichardson/node-fs-extra/issues/182    "ensureDir(dir, callback) second callback parameter not specified"
+[#181]: https://github.com/jprichardson/node-fs-extra/issues/181    "Add ability to remove file securely [enhancement, wont-fix]"
+[#180]: https://github.com/jprichardson/node-fs-extra/issues/180    "Filter option doesn't work the same way in copy and copySync [bug, feature-copy]"
+[#179]: https://github.com/jprichardson/node-fs-extra/issues/179    "Include opendir"
+[#178]: https://github.com/jprichardson/node-fs-extra/issues/178    "ENOTEMPTY is thrown on removeSync "
+[#177]: https://github.com/jprichardson/node-fs-extra/issues/177    "fix `remove()` wildcards (introduced by rimraf) [feature-remove]"
+[#176]: https://github.com/jprichardson/node-fs-extra/issues/176    "createOutputStream doesn't emit 'end' event"
+[#175]: https://github.com/jprichardson/node-fs-extra/issues/175    "[Feature Request].moveSync support [feature-move, future]"
+[#174]: https://github.com/jprichardson/node-fs-extra/pull/174      "Fix copy formatting and document options.filter"
+[#173]: https://github.com/jprichardson/node-fs-extra/issues/173    "Feature Request: writeJson should mkdirs"
+[#172]: https://github.com/jprichardson/node-fs-extra/issues/172    "rename `clobber` flags to `overwrite`"
+[#171]: https://github.com/jprichardson/node-fs-extra/issues/171    "remove unnecessary aliases"
+[#170]: https://github.com/jprichardson/node-fs-extra/pull/170      "More robust handling of errors moving across virtual drives"
+[#169]: https://github.com/jprichardson/node-fs-extra/pull/169      "suppress ensureLink & ensureSymlink dest exists error"
+[#168]: https://github.com/jprichardson/node-fs-extra/pull/168      "suppress ensurelink dest exists error"
+[#167]: https://github.com/jprichardson/node-fs-extra/pull/167      "Adds basic (string, buffer) support for ensureFile content [future]"
+[#166]: https://github.com/jprichardson/node-fs-extra/pull/166      "Adds basic (string, buffer) support for ensureFile content"
+[#165]: https://github.com/jprichardson/node-fs-extra/pull/165      "ensure for link & symlink"
+[#164]: https://github.com/jprichardson/node-fs-extra/issues/164    "Feature Request: ensureFile to take optional argument for file content"
+[#163]: https://github.com/jprichardson/node-fs-extra/issues/163    "ouputJson not formatted out of the box [bug]"
+[#162]: https://github.com/jprichardson/node-fs-extra/pull/162      "ensure symlink & link"
+[#161]: https://github.com/jprichardson/node-fs-extra/pull/161      "ensure symlink & link"
+[#160]: https://github.com/jprichardson/node-fs-extra/pull/160      "ensure symlink & link"
+[#159]: https://github.com/jprichardson/node-fs-extra/pull/159      "ensure symlink & link"
+[#158]: https://github.com/jprichardson/node-fs-extra/issues/158    "Feature Request: ensureLink and ensureSymlink methods"
+[#157]: https://github.com/jprichardson/node-fs-extra/issues/157    "writeJson isn't formatted"
+[#156]: https://github.com/jprichardson/node-fs-extra/issues/156    "Promise.promisifyAll doesn't work for some methods"
+[#155]: https://github.com/jprichardson/node-fs-extra/issues/155    "Readme"
+[#154]: https://github.com/jprichardson/node-fs-extra/issues/154    "/tmp/millis-test-sync"
+[#153]: https://github.com/jprichardson/node-fs-extra/pull/153      "Make preserveTimes also work on read-only files. Closes #152"
+[#152]: https://github.com/jprichardson/node-fs-extra/issues/152    "fs.copy fails for read-only files with preserveTimestamp=true [feature-copy]"
+[#151]: https://github.com/jprichardson/node-fs-extra/issues/151    "TOC does not work correctly on npm [documentation]"
+[#150]: https://github.com/jprichardson/node-fs-extra/issues/150    "Remove test file fixtures, create with code."
+[#149]: https://github.com/jprichardson/node-fs-extra/issues/149    "/tmp/millis-test-sync"
+[#148]: https://github.com/jprichardson/node-fs-extra/issues/148    "split out `Sync` methods in documentation"
+[#147]: https://github.com/jprichardson/node-fs-extra/issues/147    "Adding rmdirIfEmpty"
+[#146]: https://github.com/jprichardson/node-fs-extra/pull/146      "ensure test.js works"
+[#145]: https://github.com/jprichardson/node-fs-extra/issues/145    "Add `fs.exists` and `fs.existsSync` if it doesn't exist."
+[#144]: https://github.com/jprichardson/node-fs-extra/issues/144    "tests failing"
+[#143]: https://github.com/jprichardson/node-fs-extra/issues/143    "update graceful-fs"
+[#142]: https://github.com/jprichardson/node-fs-extra/issues/142    "PrependFile Feature"
+[#141]: https://github.com/jprichardson/node-fs-extra/pull/141      "Add option to preserve timestamps"
+[#140]: https://github.com/jprichardson/node-fs-extra/issues/140    "Json file reading fails with 'utf8'"
+[#139]: https://github.com/jprichardson/node-fs-extra/pull/139      "Preserve file timestamp on copy. Closes #138"
+[#138]: https://github.com/jprichardson/node-fs-extra/issues/138    "Preserve timestamps on copying files"
+[#137]: https://github.com/jprichardson/node-fs-extra/issues/137    "outputFile/outputJson: Unexpected end of input"
+[#136]: https://github.com/jprichardson/node-fs-extra/pull/136      "Update license attribute"
+[#135]: https://github.com/jprichardson/node-fs-extra/issues/135    "emptyDir throws Error if no callback is provided"
+[#134]: https://github.com/jprichardson/node-fs-extra/pull/134      "Handle EEXIST error when clobbering dir"
+[#133]: https://github.com/jprichardson/node-fs-extra/pull/133      "Travis runs with `sudo: false`"
+[#132]: https://github.com/jprichardson/node-fs-extra/pull/132      "isDirectory method"
+[#131]: https://github.com/jprichardson/node-fs-extra/issues/131    "copySync is not working iojs 1.8.4 on linux [feature-copy]"
+[#130]: https://github.com/jprichardson/node-fs-extra/pull/130      "Please review additional features."
+[#129]: https://github.com/jprichardson/node-fs-extra/pull/129      "can you review this feature?"
+[#128]: https://github.com/jprichardson/node-fs-extra/issues/128    "fsExtra.move(filepath, newPath) broken;"
+[#127]: https://github.com/jprichardson/node-fs-extra/issues/127    "consider using fs.access to remove deprecated warnings for fs.exists"
+[#126]: https://github.com/jprichardson/node-fs-extra/issues/126    " TypeError: Object #<Object> has no method 'access'"
+[#125]: https://github.com/jprichardson/node-fs-extra/issues/125    "Question: What do the *Sync function do different from non-sync"
+[#124]: https://github.com/jprichardson/node-fs-extra/issues/124    "move with clobber option 'ENOTEMPTY'"
+[#123]: https://github.com/jprichardson/node-fs-extra/issues/123    "Only copy the content of a directory"
+[#122]: https://github.com/jprichardson/node-fs-extra/pull/122      "Update section links in README to match current section ids."
+[#121]: https://github.com/jprichardson/node-fs-extra/issues/121    "emptyDir is undefined"
+[#120]: https://github.com/jprichardson/node-fs-extra/issues/120    "usage bug caused by shallow cloning methods of 'graceful-fs'"
+[#119]: https://github.com/jprichardson/node-fs-extra/issues/119    "mkdirs and ensureDir never invoke callback and consume CPU indefinitely if provided a path with invalid characters on Windows"
+[#118]: https://github.com/jprichardson/node-fs-extra/pull/118      "createOutputStream"
+[#117]: https://github.com/jprichardson/node-fs-extra/pull/117      "Fixed issue with slash separated paths on windows"
+[#116]: https://github.com/jprichardson/node-fs-extra/issues/116    "copySync can only copy directories not files [documentation, feature-copy]"
+[#115]: https://github.com/jprichardson/node-fs-extra/issues/115    ".Copy & .CopySync [feature-copy]"
+[#114]: https://github.com/jprichardson/node-fs-extra/issues/114    "Fails to move (rename) directory to non-empty directory even with clobber: true"
+[#113]: https://github.com/jprichardson/node-fs-extra/issues/113    "fs.copy seems to callback early if the destination file already exists"
+[#112]: https://github.com/jprichardson/node-fs-extra/pull/112      "Copying a file into an existing directory"
+[#111]: https://github.com/jprichardson/node-fs-extra/pull/111      "Moving a file into an existing directory "
+[#110]: https://github.com/jprichardson/node-fs-extra/pull/110      "Moving a file into an existing directory"
+[#109]: https://github.com/jprichardson/node-fs-extra/issues/109    "fs.move across windows drives fails"
+[#108]: https://github.com/jprichardson/node-fs-extra/issues/108    "fse.move directories across multiple devices doesn't work"
+[#107]: https://github.com/jprichardson/node-fs-extra/pull/107      "Check if dest path is an existing dir and copy or move source in it"
+[#106]: https://github.com/jprichardson/node-fs-extra/issues/106    "fse.copySync crashes while copying across devices D: [feature-copy]"
+[#105]: https://github.com/jprichardson/node-fs-extra/issues/105    "fs.copy hangs on iojs"
+[#104]: https://github.com/jprichardson/node-fs-extra/issues/104    "fse.move deletes folders [bug]"
+[#103]: https://github.com/jprichardson/node-fs-extra/issues/103    "Error: EMFILE with copy"
+[#102]: https://github.com/jprichardson/node-fs-extra/issues/102    "touch / touchSync was removed ?"
+[#101]: https://github.com/jprichardson/node-fs-extra/issues/101    "fs-extra promisified"
+[#100]: https://github.com/jprichardson/node-fs-extra/pull/100      "copy: options object or filter to pass to ncp"
+[#99]: https://github.com/jprichardson/node-fs-extra/issues/99      "ensureDir() modes [future]"
+[#98]: https://github.com/jprichardson/node-fs-extra/issues/98      "fs.copy() incorrect async behavior [bug]"
+[#97]: https://github.com/jprichardson/node-fs-extra/pull/97        "use path.join; fix copySync bug"
+[#96]: https://github.com/jprichardson/node-fs-extra/issues/96      "destFolderExists in copySync is always undefined."
+[#95]: https://github.com/jprichardson/node-fs-extra/pull/95        "Using graceful-ncp instead of ncp"
+[#94]: https://github.com/jprichardson/node-fs-extra/issues/94      "Error: EEXIST, file already exists '../mkdirp/bin/cmd.js' on fs.copySync() [enhancement, feature-copy]"
+[#93]: https://github.com/jprichardson/node-fs-extra/issues/93      "Confusing error if drive not mounted [enhancement]"
+[#92]: https://github.com/jprichardson/node-fs-extra/issues/92      "Problems with Bluebird"
+[#91]: https://github.com/jprichardson/node-fs-extra/issues/91      "fs.copySync('/test', '/haha') is different with 'cp -r /test /haha' [enhancement]"
+[#90]: https://github.com/jprichardson/node-fs-extra/issues/90      "Folder creation and file copy is Happening in 64 bit machine but not in 32 bit machine"
+[#89]: https://github.com/jprichardson/node-fs-extra/issues/89      "Error: EEXIST using fs-extra's fs.copy to copy a directory on Windows"
+[#88]: https://github.com/jprichardson/node-fs-extra/issues/88      "Stacking those libraries"
+[#87]: https://github.com/jprichardson/node-fs-extra/issues/87      "createWriteStream + outputFile = ?"
+[#86]: https://github.com/jprichardson/node-fs-extra/issues/86      "no moveSync?"
+[#85]: https://github.com/jprichardson/node-fs-extra/pull/85        "Copy symlinks in copySync"
+[#84]: https://github.com/jprichardson/node-fs-extra/issues/84      "Push latest version to npm ?"
+[#83]: https://github.com/jprichardson/node-fs-extra/issues/83      "Prevent copying a directory into itself [feature-copy]"
+[#82]: https://github.com/jprichardson/node-fs-extra/pull/82        "README updates for move"
+[#81]: https://github.com/jprichardson/node-fs-extra/issues/81      "fd leak after fs.move"
+[#80]: https://github.com/jprichardson/node-fs-extra/pull/80        "Preserve file mode in copySync"
+[#79]: https://github.com/jprichardson/node-fs-extra/issues/79      "fs.copy only .html file empty"
+[#78]: https://github.com/jprichardson/node-fs-extra/pull/78        "copySync was not applying filters to directories"
+[#77]: https://github.com/jprichardson/node-fs-extra/issues/77      "Create README reference to bluebird"
+[#76]: https://github.com/jprichardson/node-fs-extra/issues/76      "Create README reference to typescript"
+[#75]: https://github.com/jprichardson/node-fs-extra/issues/75      "add glob as a dep? [question]"
+[#74]: https://github.com/jprichardson/node-fs-extra/pull/74        "including new emptydir module"
+[#73]: https://github.com/jprichardson/node-fs-extra/pull/73        "add dependency status in readme"
+[#72]: https://github.com/jprichardson/node-fs-extra/pull/72        "Use svg instead of png to get better image quality"
+[#71]: https://github.com/jprichardson/node-fs-extra/issues/71      "fse.copy not working on Windows 7 x64 OS, but, copySync does work"
+[#70]: https://github.com/jprichardson/node-fs-extra/issues/70      "Not filter each file, stops on first false [bug]"
+[#69]: https://github.com/jprichardson/node-fs-extra/issues/69      "How to check if folder exist and read the folder name"
+[#68]: https://github.com/jprichardson/node-fs-extra/issues/68      "consider flag to readJsonSync (throw false) [enhancement]"
+[#67]: https://github.com/jprichardson/node-fs-extra/issues/67      "docs for readJson incorrectly states that is accepts options"
+[#66]: https://github.com/jprichardson/node-fs-extra/issues/66      "ENAMETOOLONG"
+[#65]: https://github.com/jprichardson/node-fs-extra/issues/65      "exclude filter in fs.copy"
+[#64]: https://github.com/jprichardson/node-fs-extra/issues/64      "Announce: mfs - monitor your fs-extra calls"
+[#63]: https://github.com/jprichardson/node-fs-extra/issues/63      "Walk"
+[#62]: https://github.com/jprichardson/node-fs-extra/issues/62      "npm install fs-extra doesn't work"
+[#61]: https://github.com/jprichardson/node-fs-extra/issues/61      "No longer supports node 0.8 due to use of `^` in package.json dependencies"
+[#60]: https://github.com/jprichardson/node-fs-extra/issues/60      "chmod & chown for mkdirs"
+[#59]: https://github.com/jprichardson/node-fs-extra/issues/59      "Consider including mkdirp and making fs-extra "--use_strict" safe [question]"
+[#58]: https://github.com/jprichardson/node-fs-extra/issues/58      "Stack trace not included in fs.copy error"
+[#57]: https://github.com/jprichardson/node-fs-extra/issues/57      "Possible to include wildcards in delete?"
+[#56]: https://github.com/jprichardson/node-fs-extra/issues/56      "Crash when have no access to write to destination file in copy "
+[#55]: https://github.com/jprichardson/node-fs-extra/issues/55      "Is it possible to have any console output similar to Grunt copy module?"
+[#54]: https://github.com/jprichardson/node-fs-extra/issues/54      "`copy` does not preserve file ownership and permissons"
+[#53]: https://github.com/jprichardson/node-fs-extra/issues/53      "outputFile() - ability to write data in appending mode"
+[#52]: https://github.com/jprichardson/node-fs-extra/pull/52        "This fixes (what I think) is a bug in copySync"
+[#51]: https://github.com/jprichardson/node-fs-extra/pull/51        "Add a Bitdeli Badge to README"
+[#50]: https://github.com/jprichardson/node-fs-extra/issues/50      "Replace mechanism in createFile"
+[#49]: https://github.com/jprichardson/node-fs-extra/pull/49        "update rimraf to v2.2.6"
+[#48]: https://github.com/jprichardson/node-fs-extra/issues/48      "fs.copy issue [bug]"
+[#47]: https://github.com/jprichardson/node-fs-extra/issues/47      "Bug in copy - callback called on readStream "close" - Fixed in ncp 0.5.0"
+[#46]: https://github.com/jprichardson/node-fs-extra/pull/46        "update copyright year"
+[#45]: https://github.com/jprichardson/node-fs-extra/pull/45        "Added note about fse.outputFile() being the one that overwrites"
+[#44]: https://github.com/jprichardson/node-fs-extra/pull/44        "Proposal: Stream support"
+[#43]: https://github.com/jprichardson/node-fs-extra/issues/43      "Better error reporting "
+[#42]: https://github.com/jprichardson/node-fs-extra/issues/42      "Performance issue?"
+[#41]: https://github.com/jprichardson/node-fs-extra/pull/41        "There does seem to be a synchronous version now"
+[#40]: https://github.com/jprichardson/node-fs-extra/issues/40      "fs.copy throw unexplained error ENOENT, utime "
+[#39]: https://github.com/jprichardson/node-fs-extra/pull/39        "Added regression test for copy() return callback on error"
+[#38]: https://github.com/jprichardson/node-fs-extra/pull/38        "Return err in copy() fstat cb, because stat could be undefined or null"
+[#37]: https://github.com/jprichardson/node-fs-extra/issues/37      "Maybe include a line reader? [enhancement, question]"
+[#36]: https://github.com/jprichardson/node-fs-extra/pull/36        "`filter` parameter `fs.copy` and `fs.copySync`"
+[#35]: https://github.com/jprichardson/node-fs-extra/pull/35        "`filter` parameter `fs.copy` and `fs.copySync` "
+[#34]: https://github.com/jprichardson/node-fs-extra/issues/34      "update docs to include options for JSON methods [enhancement]"
+[#33]: https://github.com/jprichardson/node-fs-extra/pull/33        "fs_extra.copySync"
+[#32]: https://github.com/jprichardson/node-fs-extra/issues/32      "update to latest jsonfile [enhancement]"
+[#31]: https://github.com/jprichardson/node-fs-extra/issues/31      "Add ensure methods [enhancement]"
+[#30]: https://github.com/jprichardson/node-fs-extra/issues/30      "update package.json optional dep `graceful-fs`"
+[#29]: https://github.com/jprichardson/node-fs-extra/issues/29      "Copy failing if dest directory doesn't exist. Is this intended?"
+[#28]: https://github.com/jprichardson/node-fs-extra/issues/28      "homepage field must be a string url. Deleted."
+[#27]: https://github.com/jprichardson/node-fs-extra/issues/27      "Update Readme"
+[#26]: https://github.com/jprichardson/node-fs-extra/issues/26      "Add readdir recursive method. [enhancement]"
+[#25]: https://github.com/jprichardson/node-fs-extra/pull/25        "adding an `.npmignore` file"
+[#24]: https://github.com/jprichardson/node-fs-extra/issues/24      "[bug] cannot run in strict mode [bug]"
+[#23]: https://github.com/jprichardson/node-fs-extra/issues/23      "`writeJSON()` should create parent directories"
+[#22]: https://github.com/jprichardson/node-fs-extra/pull/22        "Add a limit option to mkdirs()"
+[#21]: https://github.com/jprichardson/node-fs-extra/issues/21      "touch() in 0.10.0"
+[#20]: https://github.com/jprichardson/node-fs-extra/issues/20      "fs.remove yields callback before directory is really deleted"
+[#19]: https://github.com/jprichardson/node-fs-extra/issues/19      "fs.copy err is empty array"
+[#18]: https://github.com/jprichardson/node-fs-extra/pull/18        "Exposed copyFile Function"
+[#17]: https://github.com/jprichardson/node-fs-extra/issues/17      "Use `require("graceful-fs")` if found instead of `require("fs")`"
+[#16]: https://github.com/jprichardson/node-fs-extra/pull/16        "Update README.md"
+[#15]: https://github.com/jprichardson/node-fs-extra/issues/15      "Implement cp -r but sync aka copySync. [enhancement]"
+[#14]: https://github.com/jprichardson/node-fs-extra/issues/14      "fs.mkdirSync is broken in 0.3.1"
+[#13]: https://github.com/jprichardson/node-fs-extra/issues/13      "Thoughts on including a directory tree / file watcher? [enhancement, question]"
+[#12]: https://github.com/jprichardson/node-fs-extra/issues/12      "copyFile & copyFileSync are global"
+[#11]: https://github.com/jprichardson/node-fs-extra/issues/11      "Thoughts on including a file walker? [enhancement, question]"
+[#10]: https://github.com/jprichardson/node-fs-extra/issues/10      "move / moveFile API [enhancement]"
+[#9]: https://github.com/jprichardson/node-fs-extra/issues/9        "don't import normal fs stuff into fs-extra"
+[#8]: https://github.com/jprichardson/node-fs-extra/pull/8          "Update rimraf to latest version"
+[#6]: https://github.com/jprichardson/node-fs-extra/issues/6        "Remove CoffeeScript development dependency"
+[#5]: https://github.com/jprichardson/node-fs-extra/issues/5        "comments on naming"
+[#4]: https://github.com/jprichardson/node-fs-extra/issues/4        "version bump to 0.2"
+[#3]: https://github.com/jprichardson/node-fs-extra/pull/3          "Hi! I fixed some code for you!"
+[#2]: https://github.com/jprichardson/node-fs-extra/issues/2        "Merge with fs.extra and mkdirp"
+[#1]: https://github.com/jprichardson/node-fs-extra/issues/1        "file-extra npm !exist"
diff --git a/LICENSE b/LICENSE
index bc7481c..f109d23 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,15 +1,15 @@
 (The MIT License)
 
-Copyright (c) 2011-2014 JP Richardson
+Copyright (c) 2011-2016 JP Richardson
 
-Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files
 (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify,
  merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
 
 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 
-THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
-WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
 OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
index 279addf..8c53ff2 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,23 @@
 Node.js: fs-extra
 =================
 
-[![build status](https://secure.travis-ci.org/jprichardson/node-fs-extra.svg)](http://travis-ci.org/jprichardson/node-fs-extra)
+`fs-extra` adds file system methods that aren't included in the native `fs` module. It is a drop in replacement for `fs`.
+
+[![npm Package](https://img.shields.io/npm/v/fs-extra.svg?style=flat-square)](https://www.npmjs.org/package/fs-extra)
+[![build status](https://api.travis-ci.org/jprichardson/node-fs-extra.svg)](http://travis-ci.org/jprichardson/node-fs-extra)
+[![windows Build status](https://img.shields.io/appveyor/ci/jprichardson/node-fs-extra/master.svg?label=windows%20build)](https://ci.appveyor.com/project/jprichardson/node-fs-extra/branch/master)
 [![downloads per month](http://img.shields.io/npm/dm/fs-extra.svg)](https://www.npmjs.org/package/fs-extra)
 [![Coverage Status](https://img.shields.io/coveralls/jprichardson/node-fs-extra.svg)](https://coveralls.io/r/jprichardson/node-fs-extra)
 
-This module adds a few extra file system methods that aren't included in the native `fs` module. It is a drop in replacement for `fs`.
+<a href="https://github.com/feross/standard"><img src="https://cdn.rawgit.com/feross/standard/master/sticker.svg" alt="Standard JavaScript" width="100"></a>
 
+**NOTE (2016-04-28):** Node v0.10 will be unsupported 2016-10-01. Node v0.12 will be unsupported on 2017-04-01.
 
 
 Why?
 ----
 
-I got tired of including `mkdirp`, `rimraf`, and `cp -r` in most of my projects. 
+I got tired of including `mkdirp`, `rimraf`, and `ncp` in most of my projects.
 
 
 
@@ -31,64 +36,142 @@ Usage
 
 You don't ever need to include the original `fs` module again:
 
-```javascript
-var fs = require('fs') //this is no longer necessary
+```js
+var fs = require('fs') // this is no longer necessary
 ```
 
 you can now do this:
 
-```javascript
-var fs = require('fs-extra') //var fs = require('fs')
+```js
+var fs = require('fs-extra')
 ```
 
-or if you prefer to make it clear that you're using `fs-extra` and not `fs`, you may want 
-to do this:
+or if you prefer to make it clear that you're using `fs-extra` and not `fs`, you may want
+to name your `fs` variable `fse` like so:
 
-```javascript
-//var fs = require('fs')
+```js
 var fse = require('fs-extra')
 ```
 
 you can also keep both, but it's redundant:
 
-```javascript
+```js
 var fs = require('fs')
 var fse = require('fs-extra')
 ```
 
+Sync vs Async
+-------------
+Most methods are async by default (they take a callback with an `Error` as first argument).
+
+Sync methods on the other hand will throw if an error occurs.
+
+Example:
+
+```js
+var fs = require('fs-extra')
+
+fs.copy('/tmp/myfile', '/tmp/mynewfile', function (err) {
+  if (err) return console.error(err)
+  console.log("success!")
+});
+
+try {
+  fs.copySync('/tmp/myfile', '/tmp/mynewfile')
+  console.log("success!")
+} catch (err) {
+  console.error(err)
+}
+```
 
 
 Methods
 -------
+- [copy](#copy)
+- [copySync](#copy)
+- [emptyDir](#emptydirdir-callback)
+- [emptyDirSync](#emptydirdir-callback)
+- [ensureFile](#ensurefilefile-callback)
+- [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)
+- [outputFile](#outputfilefile-data-options-callback)
+- [outputFileSync](#outputfilefile-data-options-callback)
+- [outputJson](#outputjsonfile-data-options-callback)
+- [outputJsonSync](#outputjsonfile-data-options-callback)
+- [readJson](#readjsonfile-options-callback)
+- [readJsonSync](#readjsonfile-options-callback)
+- [remove](#removedir-callback)
+- [removeSync](#removedir-callback)
+- [walk](#walk)
+- [walkSync](#walkSyncDir)
+- [writeJson](#writejsonfile-object-options-callback)
+- [writeJsonSync](#writejsonfile-object-options-callback)
+
 
 **NOTE:** You can still use the native Node.js methods. They are copied over to `fs-extra`.
 
 
-### copy(src, dest, [options], callback)
+### copy()
+
+**copy(src, dest, [options], callback)**
+
 
 Copy a file or directory. The directory can have contents. Like `cp -r`.
 
-Sync: `copySync()`
+Options:
+- clobber (boolean): overwrite existing file or directory, default is `true`.
+- dereference (boolean): dereference symlinks, default is `false`.
+- preserveTimestamps (boolean): will set last modification and access times to the ones of the original source files, default is `false`.
+- filter: Function to filter copied files. Return `true` to include, `false` to exclude. This can also be a RegExp, however this is deprecated (See [issue #239](https://github.com/jprichardson/node-fs-extra/issues/239) for background). _Warning: `copySync` currently applies the filter only to files (see [#180](https://github.com/jprichardson/node-fs-extra/issues/180)). This will be fixed in a future release._
 
+Sync: `copySync()`
 
-Examples:
+Example:
 
-```javascript
+```js
 var fs = require('fs-extra')
 
-fs.copy('/tmp/myfile', '/tmp/mynewfile', function(err) {
+fs.copy('/tmp/myfile', '/tmp/mynewfile', function (err) {
   if (err) return console.error(err)
   console.log("success!")
-}) //copies file
+}) // copies file
 
-fs.copy('/tmp/mydir', '/tmp/mynewdir', function(err) {
+fs.copy('/tmp/mydir', '/tmp/mynewdir', function (err) {
   if (err) return console.error(err)
-  console.log("success!")
-}) //copies directory, even if it has subdirectories or files
+  console.log('success!')
+}) // copies directory, even if it has subdirectories or files
 ```
 
 
-### ensureFile(file, callback) 
+### emptyDir(dir, [callback])
+
+Ensures that a directory is empty. Deletes directory contents if the directory is not empty. If the directory does not exist, it is created. The directory itself is not deleted.
+
+Alias: `emptydir()`
+
+Sync: `emptyDirSync()`, `emptydirSync()`
+
+Example:
+
+```js
+var fs = require('fs-extra')
+
+// assume this directory has a lot of files and folders
+fs.emptyDir('/tmp/some/dir', function (err) {
+  if (!err) console.log('success!')
+})
+```
+
+
+### 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**.
 
@@ -99,18 +182,18 @@ Sync: `createFileSync()`,`ensureFileSync()`
 
 Example:
 
-```javascript
+```js
 var fs = require('fs-extra')
 
 var file = '/tmp/this/path/does/not/exist/file.txt'
-fs.ensureFile(file, function(err) {
+fs.ensureFile(file, function (err) {
   console.log(err) // => null
-  //file has now been created, including the directory it is to be placed in
+  // file has now been created, including the directory it is to be placed in
 })
 ```
 
 
-### ensureDir(dir, callback) 
+### ensureDir(dir, callback)
 
 Ensures that the directory exists. If the directory structure does not exist, it is created.
 
@@ -119,19 +202,60 @@ Sync: `ensureDirSync()`
 
 Example:
 
-```javascript
+```js
 var fs = require('fs-extra')
 
 var dir = '/tmp/this/path/does/not/exist'
-fs.ensureDir(dir, function(err) {
+fs.ensureDir(dir, function (err) {
+  console.log(err) // => null
+  // dir has now been created, including the directory it is to be placed in
+})
+```
+
+
+### 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
-  //dir has now been created, including the directory it is to be placed in
+  // 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) 
+
+### mkdirs(dir, callback)
 
 Creates a directory. If the parent hierarchy doesn't exist, it's created. Like `mkdir -p`.
 
@@ -142,10 +266,10 @@ Sync: `mkdirsSync()` / `mkdirpSync()`
 
 Examples:
 
-```javascript
+```js
 var fs = require('fs-extra')
 
-fs.mkdirs('/tmp/some/long/path/that/prob/doesnt/exist', function(err) {
+fs.mkdirs('/tmp/some/long/path/that/prob/doesnt/exist', function (err) {
   if (err) return console.error(err)
   console.log("success!")
 })
@@ -158,39 +282,39 @@ fs.mkdirsSync('/tmp/another/path')
 
 Moves a file or directory, even across devices.
 
-Options:  
-clobber (boolean): overwrite existing file or directory  
-limit (number): number of concurrent moves, see ncp for more information
+Options:
+- clobber (boolean): overwrite existing file or directory
+- limit (number): number of concurrent moves, see ncp for more information
 
 Example:
 
-```javascript
+```js
 var fs = require('fs-extra')
 
-fs.move('/tmp/somefile', '/tmp/does/not/exist/yet/somefile', function(err) {
+fs.move('/tmp/somefile', '/tmp/does/not/exist/yet/somefile', function (err) {
   if (err) return console.error(err)
   console.log("success!")
 })
 ```
 
 
-### outputFile(file, data, callback)
+### outputFile(file, data, [options], callback)
 
-Almost the same as `writeFile` (i.e. it [overwrites](http://pages.citebite.com/v2o5n8l2f5reb)), except that if the parent directory does not exist, it's created.
+Almost the same as `writeFile` (i.e. it [overwrites](http://pages.citebite.com/v2o5n8l2f5reb)), except that if the parent directory does not exist, it's created. `options` are what you'd pass to [`fs.writeFile()`](https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback).
 
 Sync: `outputFileSync()`
 
 
 Example:
 
-```javascript
+```js
 var fs = require('fs-extra')
 var file = '/tmp/this/path/does/not/exist/file.txt'
 
-fs.outputFile(file, 'hello!', function(err) {
+fs.outputFile(file, 'hello!', function (err) {
   console.log(err) // => null
 
-  fs.readFile(file, 'utf8', function(err, data) {
+  fs.readFile(file, 'utf8', function (err, data) {
     console.log(data) // => hello!
   })
 })
@@ -198,9 +322,10 @@ fs.outputFile(file, 'hello!', function(err) {
 
 
 
-### outputJson(file, data, callback)
+### outputJson(file, data, [options], callback)
 
 Almost the same as `writeJson`, except that if the directory does not exist, it's created.
+`options` are what you'd pass to [`jsonFile.writeFile()`](https://github.com/jprichardson/node-jsonfile#writefilefilename-options-callback).
 
 Alias: `outputJSON()`
 
@@ -209,11 +334,11 @@ Sync: `outputJsonSync()`, `outputJSONSync()`
 
 Example:
 
-```javascript
+```js
 var fs = require('fs-extra')
 var file = '/tmp/this/path/does/not/exist/file.txt'
 
-fs.outputJson(file, {name: 'JP'}, function(err) {
+fs.outputJson(file, {name: 'JP'}, function (err) {
   console.log(err) // => null
 
   fs.readJson(file, function(err, data) {
@@ -224,9 +349,10 @@ fs.outputJson(file, {name: 'JP'}, function(err) {
 
 
 
-### readJson(file, [options], callback) 
+### readJson(file, [options], callback)
 
-Reads a JSON file and then parses it into an object. `options` are the same that you'd pass to `fs.readFile`.
+Reads a JSON file and then parses it into an object. `options` are the same
+that you'd pass to [`jsonFile.readFile`](https://github.com/jprichardson/node-jsonfile#readfilefilename-options-callback).
 
 Alias: `readJSON()`
 
@@ -235,10 +361,10 @@ Sync: `readJsonSync()`, `readJSONSync()`
 
 Example:
 
-```javascript
+```js
 var fs = require('fs-extra')
 
-fs.readJson('./package.json', function(err, packageObj) {
+fs.readJson('./package.json', function (err, packageObj) {
   console.log(packageObj.version) // => 0.1.3
 })
 ```
@@ -248,7 +374,7 @@ fs.readJson('./package.json', function(err, packageObj) {
 ```js
 var fs = require('fs-extra')
 var file = path.join('/tmp/some-invalid.json')
-var data = "{not valid JSON"
+var data = '{not valid JSON'
 fs.writeFileSync(file, data)
 
 var obj = fs.readJsonSync(file, {throws: false})
@@ -260,30 +386,87 @@ console.log(obj) // => null
 
 Removes a file or directory. The directory can have contents. Like `rm -rf`.
 
-Alias: `delete()`
-
-Sync: `removeSync()` / `deleteSync()`
+Sync: `removeSync()`
 
 
 Examples:
 
-```javascript
+```js
 var fs = require('fs-extra')
 
-fs.remove('/tmp/myfile', function(err) {
+fs.remove('/tmp/myfile', function (err) {
   if (err) return console.error(err)
-  
-  console.log("success!")
+
+  console.log('success!')
 })
 
-fs.removeSync('/home/jprichardson') //I just deleted my entire HOME directory. 
+fs.removeSync('/home/jprichardson') //I just deleted my entire HOME directory.
+```
+
+### walk()
+
+**walk(dir, [streamOptions])**
+
+The function `walk()` from the module [`klaw`](https://github.com/jprichardson/node-klaw).
+
+Returns a [Readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable) that iterates
+through every file and directory starting with `dir` as the root. Every `read()` or `data` event
+returns an object with two properties: `path` and `stats`. `path` is the full path of the file and
+`stats` is an instance of [fs.Stats](https://nodejs.org/api/fs.html#fs_class_fs_stats).
+
+Streams 1 (push) example:
+
+```js
+var fs = require('fs-extra')
+var items = [] // files, directories, symlinks, etc
+fs.walk(TEST_DIR)
+  .on('data', function (item) {
+    items.push(item.path)
+  })
+  .on('end', function () {
+    console.dir(items) // => [ ... array of files]
+  })
 ```
 
+Streams 2 & 3 (pull) example:
+
+```js
+var items = [] // files, directories, symlinks, etc
+var fs = require('fs-extra')
+fs.walk(TEST_DIR)
+  .on('readable', function () {
+    var item
+    while ((item = this.read())) {
+      items.push(item.path)
+    }
+  })
+  .on('end', function () {
+    console.dir(items) // => [ ... array of files]
+  })
+```
+
+If you're not sure of the differences on Node.js streams 1, 2, 3 then I'd
+recommend this resource as a good starting point: https://strongloop.com/strongblog/whats-new-io-js-beta-streams3/.
+
+**See [`klaw` documentation](https://github.com/jprichardson/node-klaw) for more detailed usage.**
+
+### walkSync(dir)
+
+Lists all files inside a directory recursively
+
+Examples:
+
+```js
+var fs = require('fs-extra')
 
+var files = fs.walkSync('/home/jprichardson')
+// files = ['/home/jprichardson/file1', '/home/jprichardson/dir1/file2']
+```
 
-### writeJson(file, object, [options], callback) 
+### writeJson(file, object, [options], callback)
 
-Writes an object to a JSON file. `options` are the same that you'd pass to `fs.readFile`.
+Writes an object to a JSON file. `options` are the same that
+you'd pass to [`jsonFile.writeFile()`](https://github.com/jprichardson/node-jsonfile#writefilefilename-options-callback).
 
 Alias: `writeJSON()`
 
@@ -291,9 +474,9 @@ Sync: `writeJsonSync()`, `writeJSONSync()`
 
 Example:
 
-```javascript
+```js
 var fs = require('fs-extra')
-fs.writeJson('./package.json', {name: 'fs-extra'}, function(err) {
+fs.writeJson('./package.json', {name: 'fs-extra'}, function (err) {
   console.log(err)
 })
 ```
@@ -308,8 +491,8 @@ Use [Bluebird](https://github.com/petkaantonov/bluebird). See https://github.com
 explicitly listed as supported.
 
 ```js
-var Promise = require("bluebird")
-var fs = Promise.promisifyAll(require("fs-extra"))
+var Promise = require('bluebird')
+var fs = Promise.promisifyAll(require('fs-extra'))
 ```
 
 Or you can use the package [`fs-extra-promise`](https://github.com/overlookmotel/fs-extra-promise) that marries the two together.
@@ -331,6 +514,47 @@ If you want to watch for changes to files or directories, then you should use [c
 
 
 
+Hacking on fs-extra
+-------------------
+
+Wanna hack on `fs-extra`? Great! Your help is needed! [fs-extra is one of the most depended upon Node.js packages](http://nodei.co/npm/fs-extra.png?downloads=true&downloadRank=true&stars=true). This project
+uses [JavaScript Standard Style](https://github.com/feross/standard) - if the name or style choices bother you,
+you're gonna have to get over it :) If `standard` is good enough for `npm`, it's good enough for `fs-extra`.
+
+[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
+
+What's needed?
+- First, take a look at existing issues. Those are probably going to be where the priority lies.
+- More tests for edge cases. Specifically on different platforms. There can never be enough tests.
+- Improve test coverage. See coveralls output for more info.
+- After the directory walker is integrated, any function that needs to traverse directories like
+`copy`, `remove`, or `mkdirs` should be built on top of it.
+
+Note: If you make any big changes, **you should definitely file an issue for discussion first.**
+
+### Running the Test Suite
+
+fs-extra contains hundreds of tests.
+
+- `npm run lint`: runs the linter ([standard](http://standardjs.com/))
+- `npm run unit`: runs the unit tests
+- `npm test`: runs both the linter and the tests
+
+
+### Windows
+
+If you run the tests on the Windows and receive a lot of symbolic link `EPERM` permission errors, it's
+because on Windows you need elevated privilege to create symbolic links. You can add this to your Windows's
+account by following the instructions here: http://superuser.com/questions/104845/permission-to-make-symbolic-links-in-windows-7
+However, I didn't have much luck doing this.
+
+Since I develop on Mac OS X, I use VMWare Fusion for Windows testing. I create a shared folder that I map to a drive on Windows.
+I open the `Node.js command prompt` and run as `Administrator`. I then map the network drive running the following command:
+
+    net use z: "\\vmware-host\Shared Folders"
+
+I can then navigate to my `fs-extra` directory and run the tests.
+
 
 Naming
 ------
@@ -348,9 +572,9 @@ For example, `fs.readFile()` and `fs.readdir()`: the **F** is capitalized in *Fi
 
 We have a dilemma though. How do you consistently name methods that perform the following POSIX commands: `cp`, `cp -r`, `mkdir -p`, and `rm -rf`?
 
-My perspective: when in doubt, err on the side of simplicity. A directory is just a hierarchical grouping of directories and files. Consider that for a moment. So when you want to copy it or remove it, in most cases you'll want to copy or remove all of its contents. When you want to create a directory, if the directory that it's suppose to be contained in does not exist, then in most cases you'll want to create that too. 
+My perspective: when in doubt, err on the side of simplicity. A directory is just a hierarchical grouping of directories and files. Consider that for a moment. So when you want to copy it or remove it, in most cases you'll want to copy or remove all of its contents. When you want to create a directory, if the directory that it's suppose to be contained in does not exist, then in most cases you'll want to create that too.
 
-So, if you want to remove a file or a directory regardless of whether it has contents, just call `fs.remove(path)` or its alias `fs.delete(path)`. If you want to copy a file or a directory whether it has contents, just call `fs.copy(source, destination)`. If you want to create a directory regardless of whether its parent directories exist, just call `fs.mkdirs(path)` or `fs.mkdirp(path)`. 
+So, if you want to remove a file or a directory regardless of whether it has contents, just call `fs.remove(path)`. If you want to copy a file or a directory whether it has contents, just call `fs.copy(source, destination)`. If you want to create a directory regardless of whether its parent directories exist, just call `fs.mkdirs(path)` or `fs.mkdirp(path)`.
 
 
 Credit
@@ -364,48 +588,16 @@ Credit
 - [Andrew Kelley](https://github.com/andrewrk)
 
 
-Contributions
--------------
-
-If you want to contribute, please add a test. Also, don't change the version in `package.json`.
-
-
-### Contributors
-
-- [*] [JP Richardson](https://github.com/jprichardson)
-- [*] [Mike McNeil](https://github.com/mikermcneil)
-- [*] [Ian Crowther](https://github.com/iancrowther)
-- [*] [Stephen Mathieson](https://github.com/stephenmathieson)
-- [*] [Srirangan](https://github.com/Srirangan)
-- [*] [Uli Köhler](https://github.com/ulikoehler)
-- [2] [Sylvain Cleymans](https://github.com/Ackar)
-- [1] [Jim Higson](https://github.com/jimhigson)
-- [1] [PatrickJS](https://github.com/gdi2290)
-- [1] [Michael Tiller](https://github.com/xogeny)
-- [1] [yibuyisheng](https://github.com/yibuyisheng)
-- [1] [overlookmotel](https://github.com/overlookmotel)
-- `<your name here>`
-
-
 
 
 License
 -------
 
-
 Licensed under MIT
 
-Copyright (c) 2011-2014 JP Richardson
+Copyright (c) 2011-2016 [JP Richardson](https://github.com/jprichardson)
 
-[1]: http://nodejs.org/docs/latest/api/fs.html 
+[1]: http://nodejs.org/docs/latest/api/fs.html
 
 
 [jsonfile]: https://github.com/jprichardson/node-jsonfile
-
-
-
-
-
-
-
-
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..591f933
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,26 @@
+# Test against this version of Node.js
+environment:
+  matrix:
+    # node.js
+    - nodejs_version: "0.12"
+    - nodejs_version: "4"
+    - nodejs_version: "6"
+
+# Install scripts. (runs after repo cloning)
+install:
+  # Get the latest stable version of Node.js or io.js
+  - ps: Install-Product node $env:nodejs_version
+  # install modules
+  - npm config set loglevel warn
+  - npm install --silent
+
+# Post-install test scripts.
+test_script:
+  # Output useful info for debugging.
+  - node --version
+  - npm --version
+  # run tests
+  - npm test
+
+# Don't actually build.
+build: off
diff --git a/lib/__tests__/fs-integration.test.js b/lib/__tests__/fs-integration.test.js
new file mode 100644
index 0000000..efd7ba9
--- /dev/null
+++ b/lib/__tests__/fs-integration.test.js
@@ -0,0 +1,34 @@
+var assert = require('assert')
+var path = require('path')
+var os = require('os')
+var fs = require('fs')
+var fse = require('../')
+
+/* global afterEach, beforeEach, describe, it */
+
+describe('native fs', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'native-fs')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('should use native fs methods', function () {
+    var file = path.join(TEST_DIR, 'write.txt')
+    fse.writeFileSync(file, 'hello')
+    var data = fse.readFileSync(file, 'utf8')
+    assert.equal(data, 'hello')
+  })
+
+  it('should have native fs constants', function () {
+    // Node.js v0.12 / IO.js
+    if ('F_OK' in fs) {
+      assert.equal(fse.F_OK, fs.F_OK)
+    }
+  })
+})
diff --git a/lib/copy-sync/__tests__/broken-symlink.test.js b/lib/copy-sync/__tests__/broken-symlink.test.js
new file mode 100644
index 0000000..7ba184b
--- /dev/null
+++ b/lib/copy-sync/__tests__/broken-symlink.test.js
@@ -0,0 +1,59 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+var copySync = require('../copy-sync')
+
+/* global afterEach, beforeEach, describe, it */
+
+describe('copy-sync / broken symlink', function () {
+  var TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy-sync-broken-symlinks')
+  var src = path.join(TEST_DIR, 'src')
+  var out = path.join(TEST_DIR, 'out')
+
+  beforeEach(function (done) {
+    fse.emptyDir(TEST_DIR, function (err) {
+      assert.ifError(err)
+      createFixtures(src, done)
+    })
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('should copy broken symlinks by default', function () {
+    assert.doesNotThrow(function () {
+      copySync(src, out)
+    })
+
+    assert.equal(fs.readlinkSync(path.join(out, 'broken-symlink')), path.join(src, 'does-not-exist'))
+  })
+
+  it('should throw an error when dereference=true', function () {
+    assert.throws(function () {
+      copySync(src, out, {dereference: true})
+    }, function (err) {
+      return err.code === 'ENOENT'
+    })
+  })
+})
+
+function createFixtures (srcDir, callback) {
+  fs.mkdir(srcDir, function (err) {
+    if (err) return callback(err)
+
+    try {
+      var brokenFile = path.join(srcDir, 'does-not-exist')
+      var brokenFileLink = path.join(srcDir, 'broken-symlink')
+      fs.writeFileSync(brokenFile, 'does not matter')
+      fs.symlinkSync(brokenFile, brokenFileLink, 'file')
+    } catch (err) {
+      callback(err)
+    }
+
+    // break the symlink now
+    fse.remove(brokenFile, callback)
+  })
+}
diff --git a/lib/copy-sync/__tests__/copy-sync-dir.test.js b/lib/copy-sync/__tests__/copy-sync-dir.test.js
new file mode 100644
index 0000000..f29d49b
--- /dev/null
+++ b/lib/copy-sync/__tests__/copy-sync-dir.test.js
@@ -0,0 +1,131 @@
+var assert = require('assert')
+var crypto = require('crypto')
+var os = require('os')
+var path = require('path')
+var fs = require(process.cwd())
+
+/* global beforeEach, describe, it */
+
+describe('+ copySync()', function () {
+  var TEST_DIR
+  var SIZE = 16 * 64 * 1024 + 7
+  var src, dest
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy-sync-dir')
+    src = path.join(TEST_DIR, 'src')
+    dest = path.join(TEST_DIR, 'dest')
+    fs.emptyDir(TEST_DIR, done)
+  })
+
+  describe('> when the source is a directory', function () {
+    it('should copy the directory synchronously', function () {
+      var FILES = 2
+      var i, j
+      var src = path.join(TEST_DIR, 'src')
+      var dest = path.join(TEST_DIR, 'dest')
+
+      fs.mkdirsSync(src)
+
+      for (i = 0; i < FILES; ++i) {
+        fs.writeFileSync(path.join(src, i.toString()), crypto.randomBytes(SIZE))
+      }
+
+      var subdir = path.join(src, 'subdir')
+
+      fs.mkdirsSync(subdir)
+
+      for (i = 0; i < FILES; ++i) {
+        fs.writeFileSync(path.join(subdir, i.toString()), crypto.randomBytes(SIZE))
+      }
+
+      fs.copySync(src, dest)
+      assert(fs.existsSync(dest))
+
+      for (i = 0; i < FILES; ++i) {
+        assert(fs.existsSync(path.join(dest, i.toString())))
+      }
+
+      var destSub = path.join(dest, 'subdir')
+      for (j = 0; j < FILES; ++j) {
+        assert(fs.existsSync(path.join(destSub, j.toString())))
+      }
+    })
+
+    it('should preserve symbolic links', function () {
+      fs.mkdirsSync(src)
+      fs.symlinkSync('destination', path.join(src, 'symlink'))
+
+      fs.copySync(src, dest)
+
+      var link = fs.readlinkSync(path.join(dest, 'symlink'))
+      assert.strictEqual(link, 'destination')
+    })
+
+    it('should should apply filter recursively', function () {
+      var FILES = 2
+      var filter = function (s) {
+        return /0$/i.test(s)
+      }
+
+      fs.mkdirsSync(src)
+
+      for (var i = 0; i < FILES; ++i) {
+        fs.writeFileSync(path.join(src, i.toString()), crypto.randomBytes(SIZE))
+      }
+
+      var subdir = path.join(src, 'subdir')
+      fs.mkdirsSync(subdir)
+
+      for (i = 0; i < FILES; ++i) {
+        fs.writeFileSync(path.join(subdir, i.toString()), crypto.randomBytes(SIZE))
+      }
+
+      fs.copySync(src, dest, filter)
+
+      assert(fs.existsSync(dest))
+      assert(FILES > 1)
+
+      for (i = 0; i < FILES; ++i) {
+        if (i === 0) {
+          assert(fs.existsSync(path.join(dest, i.toString())))
+        } else {
+          assert(!fs.existsSync(path.join(dest, i.toString())))
+        }
+      }
+
+      var destSub = path.join(dest, 'subdir')
+
+      for (var j = 0; j < FILES; ++j) {
+        if (j === 0) {
+          assert(fs.existsSync(path.join(destSub, j.toString())))
+        } else {
+          assert(!fs.existsSync(path.join(destSub, j.toString())))
+        }
+      }
+    })
+
+    describe('> when the destination dir does not exist', function () {
+      it('should create the destination directory and copy the file', function () {
+        var src = path.join(TEST_DIR, 'data/')
+        fs.mkdirSync(src)
+
+        var d1 = 'file1'
+        var d2 = 'file2'
+
+        fs.writeFileSync(path.join(src, 'f1.txt'), d1)
+        fs.writeFileSync(path.join(src, 'f2.txt'), d2)
+
+        var dest = path.join(TEST_DIR, 'this/path/does/not/exist/outputDir')
+
+        fs.copySync(src, dest)
+
+        var o1 = fs.readFileSync(path.join(dest, 'f1.txt'), 'utf8')
+        var o2 = fs.readFileSync(path.join(dest, 'f2.txt'), 'utf8')
+
+        assert.strictEqual(d1, o1)
+        assert.strictEqual(d2, o2)
+      })
+    })
+  })
+})
diff --git a/lib/copy-sync/__tests__/copy-sync-file.test.js b/lib/copy-sync/__tests__/copy-sync-file.test.js
new file mode 100644
index 0000000..3442359
--- /dev/null
+++ b/lib/copy-sync/__tests__/copy-sync-file.test.js
@@ -0,0 +1,180 @@
+var assert = require('assert')
+var crypto = require('crypto')
+var os = require('os')
+var path = require('path')
+var fs = require(process.cwd())
+
+/* global afterEach, beforeEach, describe, it */
+
+var SIZE = 16 * 64 * 1024 + 7
+
+describe('+ copySync()', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy-sync')
+    fs.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fs.remove(TEST_DIR, done)
+  })
+
+  describe('> when the source is a file', function () {
+    it('should copy the file synchronously', function () {
+      var fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_src')
+      var fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
+
+      fs.writeFileSync(fileSrc, crypto.randomBytes(SIZE))
+
+      var srcMd5 = crypto.createHash('md5').update(fs.readFileSync(fileSrc)).digest('hex')
+      var destMd5 = ''
+
+      fs.copySync(fileSrc, fileDest)
+
+      destMd5 = crypto.createHash('md5').update(fs.readFileSync(fileDest)).digest('hex')
+      assert.strictEqual(srcMd5, destMd5)
+    })
+
+    it('should follow symlinks', function () {
+      var fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_src')
+      var fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
+      var linkSrc = path.join(TEST_DIR, 'TEST_fs-extra_copy_link')
+
+      fs.writeFileSync(fileSrc, crypto.randomBytes(SIZE))
+
+      var srcMd5 = crypto.createHash('md5').update(fs.readFileSync(fileSrc)).digest('hex')
+      var destMd5 = ''
+
+      fs.symlinkSync(fileSrc, linkSrc)
+      fs.copySync(linkSrc, fileDest)
+      destMd5 = crypto.createHash('md5').update(fs.readFileSync(fileDest)).digest('hex')
+      assert.strictEqual(srcMd5, destMd5)
+    })
+
+    it('should maintain file mode', function () {
+      var fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_src')
+      var fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
+      fs.writeFileSync(fileSrc, crypto.randomBytes(SIZE))
+
+      fs.chmodSync(fileSrc, parseInt('750', 8))
+      fs.copySync(fileSrc, fileDest)
+
+      var statSrc = fs.statSync(fileSrc)
+      var statDest = fs.statSync(fileDest)
+      assert.strictEqual(statSrc.mode, statDest.mode)
+    })
+
+    it('should only copy files allowed by filter fn', function () {
+      var srcFile1 = path.join(TEST_DIR, '1.html')
+      var srcFile2 = path.join(TEST_DIR, '2.css')
+      var srcFile3 = path.join(TEST_DIR, '3.jade')
+
+      fs.writeFileSync(srcFile1, '')
+      fs.writeFileSync(srcFile2, '')
+      fs.writeFileSync(srcFile3, '')
+
+      var destFile1 = path.join(TEST_DIR, 'dest1.html')
+      var destFile2 = path.join(TEST_DIR, 'dest2.css')
+      var destFile3 = path.join(TEST_DIR, 'dest3.jade')
+
+      var filter = function (s) { return s.split('.').pop() !== 'css' }
+
+      fs.copySync(srcFile1, destFile1, filter)
+      fs.copySync(srcFile2, destFile2, filter)
+      fs.copySync(srcFile3, destFile3, filter)
+
+      assert(fs.existsSync(destFile1))
+      assert(!fs.existsSync(destFile2))
+      assert(fs.existsSync(destFile3))
+    })
+
+    describe('> when the destination dir does not exist', function () {
+      it('should create the destination directory and copy the file', function () {
+        var src = path.join(TEST_DIR, 'file.txt')
+        var dest = path.join(TEST_DIR, 'this/path/does/not/exist/copied.txt')
+        var data = 'did it copy?\n'
+
+        fs.writeFileSync(src, data, 'utf8')
+        fs.copySync(src, dest)
+
+        var data2 = fs.readFileSync(dest, 'utf8')
+
+        assert.strictEqual(data, data2)
+      })
+    })
+
+    describe('> when clobber option is passed', function () {
+      var src, dest
+      var srcData = 'some src data'
+
+      beforeEach(function () {
+        src = path.join(TEST_DIR, 'src-file')
+        dest = path.join(TEST_DIR, 'des-file')
+
+        // source file must always exist in these cases
+        fs.writeFileSync(src, srcData)
+      })
+
+      describe('> when destination file does NOT exist', function () {
+        describe('> when clobber is true', function () {
+          it('should copy the file and not throw an error', function () {
+            fs.copySync(src, dest, {clobber: true})
+            var destData = fs.readFileSync(dest, 'utf8')
+            assert.strictEqual(srcData, destData)
+          })
+        })
+
+        describe('> when clobber is false', function () {
+          it('should copy the file and not throw an error', function () {
+            fs.copySync(src, dest, {clobber: false})
+            var destData = fs.readFileSync(dest, 'utf8')
+            assert.strictEqual(srcData, destData)
+          })
+        })
+      })
+
+      describe('when destination file does exist', function () {
+        var destData
+        beforeEach(function () {
+          destData = 'some dest data'
+          fs.writeFileSync(dest, destData)
+        })
+
+        describe('> when clobber is true', function () {
+          it('should copy the file and not throw an error', function () {
+            fs.copySync(src, dest, {clobber: true})
+            destData = fs.readFileSync(dest, 'utf8')
+            assert.strictEqual(srcData, destData)
+          })
+        })
+
+        describe('> when clobber is false', function () {
+          it('should copy the file and THROW an error', function () {
+            assert.throws(function () {
+              fs.copySync(src, dest, {clobber: false})
+            })
+
+            // copy never happened
+            var destDataNew = fs.readFileSync(dest, 'utf8')
+            assert.strictEqual(destData, destDataNew)
+          })
+        })
+
+        describe('> when clobber is true and dest is readonly', function () {
+          it('should copy the file and not throw an error', function () {
+            try {
+              fs.chmodSync(dest, parseInt('444', 8))
+              fs.copySync(src, dest, {clobber: true})
+              destData = fs.readFileSync(dest, 'utf8')
+              assert.strictEqual(srcData, destData)
+            } finally {
+              // destination file is readonly so just remove it so we don't affect other tests
+              fs.unlinkSync(dest)
+            }
+          })
+        })
+      })
+    })
+  })
+})
diff --git a/lib/copy-sync/__tests__/copy-sync-preserve-time.test.js b/lib/copy-sync/__tests__/copy-sync-preserve-time.test.js
new file mode 100644
index 0000000..8028b73
--- /dev/null
+++ b/lib/copy-sync/__tests__/copy-sync-preserve-time.test.js
@@ -0,0 +1,60 @@
+var assert = require('assert')
+var fs = require('fs')
+var os = require('os')
+var path = require('path')
+var copySync = require('../copy-sync')
+var utimes = require('../../util/utimes')
+
+/* global beforeEach, describe, it */
+
+describe('copy', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy-sync-preserve-time')
+    require(process.cwd()).emptyDir(TEST_DIR, done)
+  })
+
+  describe('> modification option', function () {
+    var SRC_FIXTURES_DIR = path.join(__dirname, './fixtures')
+    var FILES = ['a-file', path.join('a-folder', 'another-file'), path.join('a-folder', 'another-folder', 'file3')]
+
+    describe('> when modified option is turned off', function () {
+      it('should have different timestamps on copy', function () {
+        var from = path.join(SRC_FIXTURES_DIR)
+        copySync(from, TEST_DIR, {preserveTimestamps: false})
+        FILES.forEach(testFile({preserveTimestamps: false}))
+      })
+    })
+
+    describe('> when modified option is turned on', function () {
+      it('should have the same timestamps on copy', function () {
+        var from = path.join(SRC_FIXTURES_DIR)
+        copySync(from, TEST_DIR, {preserveTimestamps: true})
+        FILES.forEach(testFile({preserveTimestamps: true}))
+      })
+    })
+
+    function testFile (options) {
+      return function (file) {
+        var a = path.join(SRC_FIXTURES_DIR, file)
+        var b = path.join(TEST_DIR, file)
+        var fromStat = fs.statSync(a)
+        var toStat = fs.statSync(b)
+        if (options.preserveTimestamps) {
+          // https://github.com/nodejs/io.js/issues/2069
+          if (process.platform !== 'win32') {
+            assert.strictEqual(toStat.mtime.getTime(), fromStat.mtime.getTime())
+            assert.strictEqual(toStat.atime.getTime(), fromStat.atime.getTime())
+          } else {
+            assert.strictEqual(toStat.mtime.getTime(), utimes.timeRemoveMillis(fromStat.mtime.getTime()))
+            assert.strictEqual(toStat.atime.getTime(), utimes.timeRemoveMillis(fromStat.atime.getTime()))
+          }
+        } else {
+          assert.notEqual(toStat.mtime.getTime(), fromStat.mtime.getTime())
+          // the access time might actually be the same, so check only modification time
+        }
+      }
+    }
+  })
+})
diff --git a/test/fixtures/move/a-file b/lib/copy-sync/__tests__/fixtures/a-file
similarity index 100%
copy from test/fixtures/move/a-file
copy to lib/copy-sync/__tests__/fixtures/a-file
diff --git a/test/fixtures/move/a-folder/another-file b/lib/copy-sync/__tests__/fixtures/a-folder/another-file
similarity index 100%
copy from test/fixtures/move/a-folder/another-file
copy to lib/copy-sync/__tests__/fixtures/a-folder/another-file
diff --git a/test/fixtures/move/a-folder/another-folder/file3 b/lib/copy-sync/__tests__/fixtures/a-folder/another-folder/file3
similarity index 100%
copy from test/fixtures/move/a-folder/another-folder/file3
copy to lib/copy-sync/__tests__/fixtures/a-folder/another-folder/file3
diff --git a/lib/copy-sync/__tests__/symlink.test.js b/lib/copy-sync/__tests__/symlink.test.js
new file mode 100644
index 0000000..2890583
--- /dev/null
+++ b/lib/copy-sync/__tests__/symlink.test.js
@@ -0,0 +1,76 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+var copySync = require('../copy-sync')
+
+/* global afterEach, beforeEach, describe, it */
+
+describe('copy-sync / symlink', function () {
+  var TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy-sync-symlinks')
+  var src = path.join(TEST_DIR, 'src')
+  var out = path.join(TEST_DIR, 'out')
+
+  beforeEach(function (done) {
+    fse.emptyDir(TEST_DIR, function (err) {
+      assert.ifError(err)
+      createFixtures(src, done)
+    })
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('copies symlinks by default', function () {
+    assert.doesNotThrow(function () {
+      copySync(src, out)
+    })
+
+    assert.equal(fs.readlinkSync(path.join(out, 'file-symlink')), path.join(src, 'foo'))
+    assert.equal(fs.readlinkSync(path.join(out, 'dir-symlink')), path.join(src, 'dir'))
+  })
+
+  it('copies file contents when dereference=true', function () {
+    try {
+      copySync(src, out, {dereference: true})
+    } catch (err) {
+      assert.ifError(err)
+    }
+
+    var fileSymlinkPath = path.join(out, 'file-symlink')
+    assert.ok(fs.lstatSync(fileSymlinkPath).isFile())
+    assert.equal(fs.readFileSync(fileSymlinkPath), 'foo contents')
+
+    var dirSymlinkPath = path.join(out, 'dir-symlink')
+    assert.ok(fs.lstatSync(dirSymlinkPath).isDirectory())
+    assert.deepEqual(fs.readdirSync(dirSymlinkPath), ['bar'])
+  })
+})
+
+function createFixtures (srcDir, callback) {
+  fs.mkdir(srcDir, function (err) {
+    if (err) return callback(err)
+
+    // note: third parameter in symlinkSync is type e.g. 'file' or 'dir'
+    // https://nodejs.org/api/fs.html#fs_fs_symlink_srcpath_dstpath_type_callback
+    try {
+      var fooFile = path.join(srcDir, 'foo')
+      var fooFileLink = path.join(srcDir, 'file-symlink')
+      fs.writeFileSync(fooFile, 'foo contents')
+      fs.symlinkSync(fooFile, fooFileLink, 'file')
+
+      var dir = path.join(srcDir, 'dir')
+      var dirFile = path.join(dir, 'bar')
+      var dirLink = path.join(srcDir, 'dir-symlink')
+      fs.mkdirSync(dir)
+      fs.writeFileSync(dirFile, 'bar contents')
+      fs.symlinkSync(dir, dirLink, 'dir')
+    } catch (err) {
+      callback(err)
+    }
+
+    callback()
+  })
+}
diff --git a/lib/copy-sync/copy-file-sync.js b/lib/copy-sync/copy-file-sync.js
new file mode 100644
index 0000000..9cac892
--- /dev/null
+++ b/lib/copy-sync/copy-file-sync.js
@@ -0,0 +1,42 @@
+var fs = require('graceful-fs')
+
+var BUF_LENGTH = 64 * 1024
+var _buff = new Buffer(BUF_LENGTH)
+
+function copyFileSync (srcFile, destFile, options) {
+  var clobber = options.clobber
+  var preserveTimestamps = options.preserveTimestamps
+
+  if (fs.existsSync(destFile)) {
+    if (clobber) {
+      fs.unlinkSync(destFile)
+    } else {
+      var err = new Error('EEXIST: ' + destFile + ' already exists.')
+      err.code = 'EEXIST'
+      err.errno = -17
+      err.path = destFile
+      throw err
+    }
+  }
+
+  var fdr = fs.openSync(srcFile, 'r')
+  var stat = fs.fstatSync(fdr)
+  var fdw = fs.openSync(destFile, 'w', stat.mode)
+  var bytesRead = 1
+  var pos = 0
+
+  while (bytesRead > 0) {
+    bytesRead = fs.readSync(fdr, _buff, 0, BUF_LENGTH, pos)
+    fs.writeSync(fdw, _buff, 0, bytesRead)
+    pos += bytesRead
+  }
+
+  if (preserveTimestamps) {
+    fs.futimesSync(fdw, stat.atime, stat.mtime)
+  }
+
+  fs.closeSync(fdr)
+  fs.closeSync(fdw)
+}
+
+module.exports = copyFileSync
diff --git a/lib/copy-sync/copy-sync.js b/lib/copy-sync/copy-sync.js
new file mode 100644
index 0000000..38c8c43
--- /dev/null
+++ b/lib/copy-sync/copy-sync.js
@@ -0,0 +1,56 @@
+var fs = require('graceful-fs')
+var path = require('path')
+var copyFileSync = require('./copy-file-sync')
+var mkdir = require('../mkdirs')
+
+function copySync (src, dest, options) {
+  if (typeof options === 'function' || options instanceof RegExp) {
+    options = {filter: options}
+  }
+
+  options = options || {}
+  options.recursive = !!options.recursive
+
+  // default to true for now
+  options.clobber = 'clobber' in options ? !!options.clobber : true
+  options.dereference = 'dereference' in options ? !!options.dereference : false
+  options.preserveTimestamps = 'preserveTimestamps' in options ? !!options.preserveTimestamps : false
+
+  options.filter = options.filter || function () { return true }
+
+  // Warn about using preserveTimestamps on 32-bit node:
+  if (options.preserveTimestamps && process.arch === 'ia32') {
+    console.warn('fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n' +
+    'see https://github.com/jprichardson/node-fs-extra/issues/269')
+  }
+
+  var stats = (options.recursive && !options.dereference) ? fs.lstatSync(src) : fs.statSync(src)
+  var destFolder = path.dirname(dest)
+  var destFolderExists = fs.existsSync(destFolder)
+  var performCopy = false
+
+  if (stats.isFile()) {
+    if (options.filter instanceof RegExp) {
+      console.warn('Warning: fs-extra: Passing a RegExp filter is deprecated, use a function')
+      performCopy = options.filter.test(src)
+    } else if (typeof options.filter === 'function') performCopy = options.filter(src)
+
+    if (performCopy) {
+      if (!destFolderExists) mkdir.mkdirsSync(destFolder)
+      copyFileSync(src, dest, {clobber: options.clobber, preserveTimestamps: options.preserveTimestamps})
+    }
+  } else if (stats.isDirectory()) {
+    if (!fs.existsSync(dest)) mkdir.mkdirsSync(dest)
+    var contents = fs.readdirSync(src)
+    contents.forEach(function (content) {
+      var opts = options
+      opts.recursive = true
+      copySync(path.join(src, content), path.join(dest, content), opts)
+    })
+  } else if (options.recursive && stats.isSymbolicLink()) {
+    var srcPath = fs.readlinkSync(src)
+    fs.symlinkSync(srcPath, dest)
+  }
+}
+
+module.exports = copySync
diff --git a/lib/copy-sync/index.js b/lib/copy-sync/index.js
new file mode 100644
index 0000000..ebc7e0b
--- /dev/null
+++ b/lib/copy-sync/index.js
@@ -0,0 +1,3 @@
+module.exports = {
+  copySync: require('./copy-sync')
+}
diff --git a/lib/copy.js b/lib/copy.js
deleted file mode 100644
index 36d13bc..0000000
--- a/lib/copy.js
+++ /dev/null
@@ -1,99 +0,0 @@
-var fs = require('graceful-fs')
-var path = require('path')
-var ncp = require('./_copy').ncp
-var mkdir = require('./mkdir')
-var create = require('./create')
-
-var BUF_LENGTH = 64 * 1024
-var _buff = new Buffer(BUF_LENGTH)
-
-var copyFileSync = function(srcFile, destFile) {
-  var fdr = fs.openSync(srcFile, 'r')
-  var stat = fs.fstatSync(fdr)
-  var fdw = fs.openSync(destFile, 'w', stat.mode)
-  var bytesRead = 1
-  var pos = 0
-
-  while (bytesRead > 0) {
-    bytesRead = fs.readSync(fdr, _buff, 0, BUF_LENGTH, pos)
-    fs.writeSync(fdw, _buff, 0, bytesRead)
-    pos += bytesRead
-  }
-
-  fs.closeSync(fdr)
-  fs.closeSync(fdw)
-}
-
-function copy(src, dest, options, callback) {
-  if( typeof options == "function" && !callback) {
-    callback = options
-    options = {}
-  } else if (typeof options == "function" || options instanceof RegExp) {
-    options = {filter: options}
-  }
-  callback = callback || function(){}
-
-  fs.lstat(src, function(err, stats) {
-    if (err) return callback(err)
-
-    var dir = null
-    if (stats.isDirectory()) {
-      var parts = dest.split(path.sep)
-      parts.pop()
-      dir = parts.join(path.sep)
-    } else {
-      dir = path.dirname(dest)
-    }
-
-    fs.exists(dir, function(dirExists) {
-      if (dirExists) return ncp(src, dest, options, callback)
-      mkdir.mkdirs(dir, function(err) {
-        if (err) return callback(err)
-        ncp(src, dest, options, callback)
-      })
-    })
-  })
-}
-
-function copySync(src, dest, options) {
-  if (typeof options == "function" || options instanceof RegExp) {
-    options = {filter: options}
-  }
-
-  options = options || {}
-  options.recursive = !!options.recursive
-
-  options.filter = options.filter || function() { return true }
-
-  var stats = options.recursive ? fs.lstatSync(src) : fs.statSync(src)
-  var destFolder = path.dirname(dest)
-  var destFolderExists = fs.existsSync(destFolder)
-  var performCopy = false
-
-  if (stats.isFile()) {
-    if (options.filter instanceof RegExp) performCopy = options.filter.test(src)
-    else if (typeof options.filter == "function") performCopy = options.filter(src)
-
-    if (performCopy) {
-      if (!destFolderExists) mkdir.mkdirsSync(destFolder)
-      copyFileSync(src, dest)
-    }
-  }
-  else if (stats.isDirectory()) {
-    if (!fs.existsSync(dest)) mkdir.mkdirsSync(dest)
-    var contents = fs.readdirSync(src)
-    contents.forEach(function(content) {
-      copySync(path.join(src, content), path.join(dest, content), {filter: options.filter, recursive: true})
-    })
-  }
-  else if (options.recursive && stats.isSymbolicLink()) {
-    var srcPath = fs.readlinkSync(src)
-    fs.symlinkSync(srcPath, dest)
-  }
-}
-
-module.exports = {
-  copy: copy,
-  copySync: copySync
-}
-
diff --git a/lib/copy/__tests__/async/copy-gh-89.test.js b/lib/copy/__tests__/async/copy-gh-89.test.js
new file mode 100644
index 0000000..4ff9269
--- /dev/null
+++ b/lib/copy/__tests__/async/copy-gh-89.test.js
@@ -0,0 +1,54 @@
+// relevant: https://github.com/jprichardson/node-fs-extra/issues/89
+// come up with better file name
+
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+
+/* global afterEach, beforeEach, describe, it */
+
+describe('copy / gh #89', function () {
+  var TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy-gh-89')
+
+  beforeEach(function (done) {
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('should...', function (done) {
+    var A = path.join(TEST_DIR, 'A')
+    var B = path.join(TEST_DIR, 'B')
+    fs.mkdirSync(A)
+    fs.mkdirSync(B)
+
+    var one = path.join(A, 'one.txt')
+    var two = path.join(A, 'two.txt')
+    var three = path.join(B, 'three.txt')
+    var four = path.join(B, 'four.txt')
+
+    fs.writeFileSync(one, '1')
+    fs.writeFileSync(two, '2')
+    fs.writeFileSync(three, '3')
+    fs.writeFileSync(four, '4')
+
+    var C = path.join(TEST_DIR, 'C')
+    fse.copy(A, C, function (err) {
+      if (err) return done(err)
+
+      fse.copy(B, C, function (err) {
+        if (err) return done(err)
+
+        assert(fs.existsSync(path.join(C, 'one.txt')))
+        assert(fs.existsSync(path.join(C, 'two.txt')))
+        assert(fs.existsSync(path.join(C, 'three.txt')))
+        assert(fs.existsSync(path.join(C, 'four.txt')))
+        done()
+      })
+    })
+  })
+})
diff --git a/lib/copy/__tests__/copy-dev-null.test.js b/lib/copy/__tests__/copy-dev-null.test.js
new file mode 100644
index 0000000..dfdfea5
--- /dev/null
+++ b/lib/copy/__tests__/copy-dev-null.test.js
@@ -0,0 +1,34 @@
+var assert = require('assert')
+var fs = require('fs')
+var os = require('os')
+var path = require('path')
+var fse = require('../../')
+
+/* global afterEach, beforeEach, describe, it */
+
+var TEST_DIR = ''
+
+describe('fs-extra', function () {
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'test', 'fs-extra', 'copy-dev-null')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  describe('+ copy()', function () {
+    it('should error', function (done) {
+      // no /dev/null on windows
+      if (process.platform === 'win32') return done()
+      var tmpFile = path.join(TEST_DIR, 'foo')
+      fse.copy('/dev/null', tmpFile, function (err) {
+        assert.ifError(err)
+        var stats = fs.lstatSync(tmpFile)
+        assert.strictEqual(stats.size, 0)
+        done()
+      })
+    })
+  })
+})
diff --git a/lib/copy/__tests__/copy-permissions.test.js b/lib/copy/__tests__/copy-permissions.test.js
new file mode 100644
index 0000000..16516d6
--- /dev/null
+++ b/lib/copy/__tests__/copy-permissions.test.js
@@ -0,0 +1,108 @@
+var assert = require('assert')
+var fs = require('fs')
+var os = require('os')
+var path = require('path')
+var fse = require('../../')
+
+/* global beforeEach, describe, it */
+
+var o777 = parseInt('777', 8)
+var o666 = parseInt('666', 8)
+var o444 = parseInt('444', 8)
+
+describe('copy', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  // pretty UNIX specific, may not pass on windows... only tested on Mac OS X 10.9
+  it('should maintain file permissions and ownership', function (done) {
+    if (process.platform === 'win32') return done()
+
+    // var userid = require('userid')
+
+    // http://man7.org/linux/man-pages/man2/stat.2.html
+    var S_IFREG = parseInt('0100000', 8) // regular file
+    var S_IFDIR = parseInt('0040000', 8) // directory
+
+    // these are Mac specific I think (at least staff), should find Linux equivalent
+    var gidWheel
+    var gidStaff
+
+    try {
+      gidWheel = process.getgid() // userid.gid('wheel')
+    } catch (err) {
+      gidWheel = process.getgid()
+    }
+
+    try {
+      gidStaff = process.getgid() // userid.gid('staff')
+    } catch (err) {
+      gidStaff = process.getgid()
+    }
+
+    var permDir = path.join(TEST_DIR, 'perms')
+    fs.mkdirSync(permDir)
+
+    var srcDir = path.join(permDir, 'src')
+    fs.mkdirSync(srcDir)
+
+    var f1 = path.join(srcDir, 'f1.txt')
+    fs.writeFileSync(f1, '')
+    fs.chmodSync(f1, o666)
+    fs.chownSync(f1, process.getuid(), gidWheel)
+    var f1stats = fs.lstatSync(f1)
+    assert.strictEqual(f1stats.mode - S_IFREG, o666)
+
+    var d1 = path.join(srcDir, 'somedir')
+    fs.mkdirSync(d1)
+    fs.chmodSync(d1, o777)
+    fs.chownSync(d1, process.getuid(), gidStaff)
+    var d1stats = fs.lstatSync(d1)
+    assert.strictEqual(d1stats.mode - S_IFDIR, o777)
+
+    var f2 = path.join(d1, 'f2.bin')
+    fs.writeFileSync(f2, '')
+    fs.chmodSync(f2, o777)
+    fs.chownSync(f2, process.getuid(), gidStaff)
+    var f2stats = fs.lstatSync(f2)
+    assert.strictEqual(f2stats.mode - S_IFREG, o777)
+
+    var d2 = path.join(srcDir, 'crazydir')
+    fs.mkdirSync(d2)
+    fs.chmodSync(d2, o444)
+    fs.chownSync(d2, process.getuid(), gidWheel)
+    var d2stats = fs.lstatSync(d2)
+    assert.strictEqual(d2stats.mode - S_IFDIR, o444)
+
+    var destDir = path.join(permDir, 'dest')
+    fse.copy(srcDir, destDir, function (err) {
+      assert.ifError(err)
+
+      var newf1stats = fs.lstatSync(path.join(permDir, 'dest/f1.txt'))
+      var newd1stats = fs.lstatSync(path.join(permDir, 'dest/somedir'))
+      var newf2stats = fs.lstatSync(path.join(permDir, 'dest/somedir/f2.bin'))
+      var newd2stats = fs.lstatSync(path.join(permDir, 'dest/crazydir'))
+
+      assert.strictEqual(newf1stats.mode, f1stats.mode)
+      assert.strictEqual(newd1stats.mode, d1stats.mode)
+      assert.strictEqual(newf2stats.mode, f2stats.mode)
+      assert.strictEqual(newd2stats.mode, d2stats.mode)
+
+      assert.strictEqual(newf1stats.gid, f1stats.gid)
+      assert.strictEqual(newd1stats.gid, d1stats.gid)
+      assert.strictEqual(newf2stats.gid, f2stats.gid)
+      assert.strictEqual(newd2stats.gid, d2stats.gid)
+
+      assert.strictEqual(newf1stats.uid, f1stats.uid)
+      assert.strictEqual(newd1stats.uid, d1stats.uid)
+      assert.strictEqual(newf2stats.uid, f2stats.uid)
+      assert.strictEqual(newd2stats.uid, d2stats.uid)
+
+      done()
+    })
+  })
+})
diff --git a/lib/copy/__tests__/copy-preserve-time.test.js b/lib/copy/__tests__/copy-preserve-time.test.js
new file mode 100644
index 0000000..9de5e0c
--- /dev/null
+++ b/lib/copy/__tests__/copy-preserve-time.test.js
@@ -0,0 +1,68 @@
+var assert = require('assert')
+var fs = require('fs')
+var os = require('os')
+var path = require('path')
+var copy = require('../copy')
+var utimes = require('../../util/utimes')
+
+/* global beforeEach, describe, it */
+
+describe('copy', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy')
+    require(process.cwd()).emptyDir(TEST_DIR, done)
+  })
+
+  describe('> modification option', function () {
+    var SRC_FIXTURES_DIR = path.join(__dirname, '/fixtures')
+    var FILES = ['a-file', path.join('a-folder', 'another-file'), path.join('a-folder', 'another-folder', 'file3')]
+
+    describe('> when modified option is turned off', function () {
+      it('should have different timestamps on copy', function (done) {
+        var from = path.join(SRC_FIXTURES_DIR)
+        var to = path.join(TEST_DIR)
+
+        copy(from, to, {preserveTimestamps: false}, function () {
+          FILES.forEach(testFile({preserveTimestamps: false}))
+          done()
+        })
+      })
+    })
+
+    describe('> when modified option is turned on', function () {
+      it('should have the same timestamps on copy', function (done) {
+        var from = path.join(SRC_FIXTURES_DIR)
+        var to = path.join(TEST_DIR)
+
+        copy(from, to, {preserveTimestamps: true}, function () {
+          FILES.forEach(testFile({preserveTimestamps: true}))
+          done()
+        })
+      })
+    })
+
+    function testFile (options) {
+      return function (file) {
+        var a = path.join(SRC_FIXTURES_DIR, file)
+        var b = path.join(TEST_DIR, file)
+        var fromStat = fs.statSync(a)
+        var toStat = fs.statSync(b)
+        if (options.preserveTimestamps) {
+          // https://github.com/nodejs/io.js/issues/2069
+          if (process.platform !== 'win32') {
+            assert.strictEqual(toStat.mtime.getTime(), fromStat.mtime.getTime())
+            assert.strictEqual(toStat.atime.getTime(), fromStat.atime.getTime())
+          } else {
+            assert.strictEqual(toStat.mtime.getTime(), utimes.timeRemoveMillis(fromStat.mtime.getTime()))
+            assert.strictEqual(toStat.atime.getTime(), utimes.timeRemoveMillis(fromStat.atime.getTime()))
+          }
+        } else {
+          assert.notEqual(toStat.mtime.getTime(), fromStat.mtime.getTime())
+          // the access time might actually be the same, so check only modification time
+        }
+      }
+    }
+  })
+})
diff --git a/lib/copy/__tests__/copy.test.js b/lib/copy/__tests__/copy.test.js
new file mode 100644
index 0000000..5c1efbc
--- /dev/null
+++ b/lib/copy/__tests__/copy.test.js
@@ -0,0 +1,193 @@
+var assert = require('assert')
+var crypto = require('crypto')
+var fs = require('fs')
+var os = require('os')
+var path = require('path')
+var fse = require('../../')
+
+/* global afterEach, beforeEach, describe, it */
+
+var SIZE = 16 * 64 * 1024 + 7
+var TEST_DIR = ''
+
+describe('fs-extra', function () {
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'copy')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  describe('+ copy()', function () {
+    it('should return an error if src and dest are the same', function (done) {
+      var fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_copy')
+      var fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
+      fse.copy(fileSrc, fileDest, function (err) {
+        assert.equal(err.message, 'Source and destination must not be the same.')
+        done()
+      })
+    })
+
+    describe('> when the source is a file', function () {
+      it('should copy the file asynchronously', function (done) {
+        var fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_src')
+        var fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
+        fs.writeFileSync(fileSrc, crypto.randomBytes(SIZE))
+        var srcMd5 = crypto.createHash('md5').update(fs.readFileSync(fileSrc)).digest('hex')
+        var destMd5 = ''
+
+        fse.copy(fileSrc, fileDest, function (err) {
+          assert(!err)
+          destMd5 = crypto.createHash('md5').update(fs.readFileSync(fileDest)).digest('hex')
+          assert.strictEqual(srcMd5, destMd5)
+          done()
+        })
+      })
+
+      it('should return an error if the source file does not exist', function (done) {
+        var fileSrc = 'we-simply-assume-this-file-does-not-exist.bin'
+        var fileDest = path.join(TEST_DIR, 'TEST_fs-extra_copy')
+
+        fse.copy(fileSrc, fileDest, function (err) {
+          assert(err)
+          done()
+        })
+      })
+
+      it('should only copy files allowed by filter fn', function (done) {
+        var srcFile1 = path.join(TEST_DIR, '1.css')
+        fs.writeFileSync(srcFile1, '')
+        var destFile1 = path.join(TEST_DIR, 'dest1.css')
+        var filter = function (s) { return s.split('.').pop() !== 'css' }
+        fse.copy(srcFile1, destFile1, filter, function () {
+          assert(!fs.existsSync(destFile1))
+          done()
+        })
+      })
+
+      it('accepts options object in place of filter', function (done) {
+        var srcFile1 = path.join(TEST_DIR, '1.jade')
+        fs.writeFileSync(srcFile1, '')
+        var destFile1 = path.join(TEST_DIR, 'dest1.jade')
+        var options = {filter: function (s) { return /.html$|.css$/i.test(s) }}
+        fse.copy(srcFile1, destFile1, options, function () {
+          assert(!fs.existsSync(destFile1))
+          done()
+        })
+      })
+
+      it('should copy to a destination file with two \'$\' characters in name (eg: TEST_fs-extra_$$_copy)', function (done) {
+        var fileSrc = path.join(TEST_DIR, 'TEST_fs-extra_src')
+        var fileDest = path.join(TEST_DIR, 'TEST_fs-extra_$$_copy')
+        fs.writeFileSync(fileSrc, '')
+
+        fse.copy(fileSrc, fileDest, function (err) {
+          assert(!err)
+          fs.statSync(fileDest)
+          done()
+        })
+      })
+
+      describe('> when the destination dir does not exist', function () {
+        it('should create the destination directory and copy the file', function (done) {
+          var src = path.join(TEST_DIR, 'file.txt')
+          var dest = path.join(TEST_DIR, 'this/path/does/not/exist/copied.txt')
+          var data = 'did it copy?\n'
+
+          fs.writeFileSync(src, data, 'utf8')
+
+          fse.copy(src, dest, function (err) {
+            var data2 = fs.readFileSync(dest, 'utf8')
+            assert.strictEqual(data, data2)
+            done(err)
+          })
+        })
+      })
+    })
+
+    describe('> when the source is a directory', function () {
+      describe('> when the source directory does not exist', function () {
+        it('should return an error', function (done) {
+          var ts = path.join(TEST_DIR, 'this_dir_does_not_exist')
+          var td = path.join(TEST_DIR, 'this_dir_really_does_not_matter')
+          fse.copy(ts, td, function (err) {
+            assert(err)
+            done()
+          })
+        })
+      })
+
+      it('should copy the directory asynchronously', function (done) {
+        var FILES = 2
+        var src = path.join(TEST_DIR, 'src')
+        var dest = path.join(TEST_DIR, 'dest')
+
+        fse.mkdirs(src, function (err) {
+          assert(!err)
+          for (var i = 0; i < FILES; ++i) {
+            fs.writeFileSync(path.join(src, i.toString()), crypto.randomBytes(SIZE))
+          }
+
+          var subdir = path.join(src, 'subdir')
+          fse.mkdirs(subdir, function (err) {
+            assert(!err)
+            for (var i = 0; i < FILES; ++i) {
+              fs.writeFileSync(path.join(subdir, i.toString()), crypto.randomBytes(SIZE))
+            }
+
+            fse.copy(src, dest, function (err) {
+              assert.ifError(err)
+              assert(fs.existsSync(dest))
+
+              for (var i = 0; i < FILES; ++i) {
+                assert(fs.existsSync(path.join(dest, i.toString())))
+              }
+
+              var destSub = path.join(dest, 'subdir')
+              for (var j = 0; j < FILES; ++j) {
+                assert(fs.existsSync(path.join(destSub, j.toString())))
+              }
+
+              done()
+            })
+          })
+        })
+      })
+
+      describe('> when the destination dir does not exist', function () {
+        it('should create the destination directory and copy the file', function (done) {
+          var src = path.join(TEST_DIR, 'data/')
+          fse.mkdirsSync(src)
+          var d1 = 'file1'
+          var d2 = 'file2'
+
+          fs.writeFileSync(path.join(src, 'f1.txt'), d1)
+          fs.writeFileSync(path.join(src, 'f2.txt'), d2)
+
+          var dest = path.join(TEST_DIR, 'this/path/does/not/exist/outputDir')
+
+          fse.copy(src, dest, function (err) {
+            var o1 = fs.readFileSync(path.join(dest, 'f1.txt'), 'utf8')
+            var o2 = fs.readFileSync(path.join(dest, 'f2.txt'), 'utf8')
+
+            assert.strictEqual(d1, o1)
+            assert.strictEqual(d2, o2)
+
+            done(err)
+          })
+        })
+      })
+
+      describe('> when src dir does not exist', function () {
+        it('should return an error', function (done) {
+          fse.copy('/does/not/exist', '/something/else', function (err) {
+            assert(err instanceof Error)
+            done()
+          })
+        })
+      })
+    })
+  })
+})
diff --git a/test/fixtures/move/a-file b/lib/copy/__tests__/fixtures/a-file
similarity index 100%
copy from test/fixtures/move/a-file
copy to lib/copy/__tests__/fixtures/a-file
diff --git a/test/fixtures/move/a-folder/another-file b/lib/copy/__tests__/fixtures/a-folder/another-file
similarity index 100%
copy from test/fixtures/move/a-folder/another-file
copy to lib/copy/__tests__/fixtures/a-folder/another-file
diff --git a/test/fixtures/move/a-folder/another-folder/file3 b/lib/copy/__tests__/fixtures/a-folder/another-folder/file3
similarity index 100%
copy from test/fixtures/move/a-folder/another-folder/file3
copy to lib/copy/__tests__/fixtures/a-folder/another-folder/file3
diff --git a/lib/copy/__tests__/ncp/README.md b/lib/copy/__tests__/ncp/README.md
new file mode 100644
index 0000000..7b6c04e
--- /dev/null
+++ b/lib/copy/__tests__/ncp/README.md
@@ -0,0 +1 @@
+These tests came from: https://github.com/AvianFlu/ncp/tree/v1.0.1/test
\ No newline at end of file
diff --git a/lib/copy/__tests__/ncp/broken-symlink.test.js b/lib/copy/__tests__/ncp/broken-symlink.test.js
new file mode 100644
index 0000000..33b0d43
--- /dev/null
+++ b/lib/copy/__tests__/ncp/broken-symlink.test.js
@@ -0,0 +1,58 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+var ncp = require('../../ncp')
+
+/* global afterEach, beforeEach, describe, it */
+
+describe('ncp broken symlink', function () {
+  var TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'ncp-broken-symlinks')
+  var src = path.join(TEST_DIR, 'src')
+  var out = path.join(TEST_DIR, 'out')
+
+  beforeEach(function (done) {
+    fse.emptyDir(TEST_DIR, function (err) {
+      assert.ifError(err)
+      createFixtures(src, done)
+    })
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('should copy broken symlinks by default', function (done) {
+    ncp(src, out, function (err) {
+      if (err) return done(err)
+      assert.equal(fs.readlinkSync(path.join(out, 'broken-symlink')), path.join(src, 'does-not-exist'))
+      done()
+    })
+  })
+
+  it('should return an error when dereference=true', function (done) {
+    ncp(src, out, {dereference: true}, function (err) {
+      assert.equal(err.code, 'ENOENT')
+      done()
+    })
+  })
+})
+
+function createFixtures (srcDir, callback) {
+  fs.mkdir(srcDir, function (err) {
+    if (err) return callback(err)
+
+    try {
+      var brokenFile = path.join(srcDir, 'does-not-exist')
+      var brokenFileLink = path.join(srcDir, 'broken-symlink')
+      fs.writeFileSync(brokenFile, 'does not matter')
+      fs.symlinkSync(brokenFile, brokenFileLink, 'file')
+    } catch (err) {
+      callback(err)
+    }
+
+    // break the symlink now
+    fse.remove(brokenFile, callback)
+  })
+}
diff --git a/test/ncp/fixtures/modified-files/out/a b/lib/copy/__tests__/ncp/fixtures/modified-files/out/a
similarity index 100%
rename from test/ncp/fixtures/modified-files/out/a
rename to lib/copy/__tests__/ncp/fixtures/modified-files/out/a
diff --git a/test/ncp/fixtures/modified-files/src/a b/lib/copy/__tests__/ncp/fixtures/modified-files/src/a
similarity index 100%
rename from test/ncp/fixtures/modified-files/src/a
rename to lib/copy/__tests__/ncp/fixtures/modified-files/src/a
diff --git a/test/ncp/fixtures/regular-fixtures/out/a b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/a
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/out/a
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/a
diff --git a/test/ncp/fixtures/regular-fixtures/out/b b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/b
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/out/b
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/b
diff --git a/test/ncp/fixtures/regular-fixtures/out/c b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/c
similarity index 100%
copy from test/ncp/fixtures/regular-fixtures/out/c
copy to lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/c
diff --git a/test/ncp/fixtures/regular-fixtures/out/d b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/d
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/out/d
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/d
diff --git a/test/ncp/fixtures/regular-fixtures/out/e b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/e
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/out/e
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/e
diff --git a/test/ncp/fixtures/regular-fixtures/out/f b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/f
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/out/f
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/f
diff --git a/test/ncp/fixtures/regular-fixtures/out/sub/a b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/sub/a
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/out/sub/a
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/sub/a
diff --git a/test/ncp/fixtures/regular-fixtures/out/sub/b b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/sub/b
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/out/sub/b
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/out/sub/b
diff --git a/test/ncp/fixtures/regular-fixtures/src/a b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/a
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/src/a
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/a
diff --git a/test/ncp/fixtures/regular-fixtures/src/b b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/b
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/src/b
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/b
diff --git a/test/ncp/fixtures/regular-fixtures/src/c b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/c
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/src/c
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/c
diff --git a/test/ncp/fixtures/regular-fixtures/src/d b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/d
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/src/d
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/d
diff --git a/test/ncp/fixtures/regular-fixtures/src/e b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/e
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/src/e
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/e
diff --git a/test/ncp/fixtures/regular-fixtures/src/f b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/f
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/src/f
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/f
diff --git a/test/ncp/fixtures/regular-fixtures/src/sub/a b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/sub/a
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/src/sub/a
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/sub/a
diff --git a/test/ncp/fixtures/regular-fixtures/src/sub/b b/lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/sub/b
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/src/sub/b
rename to lib/copy/__tests__/ncp/fixtures/regular-fixtures/src/sub/b
diff --git a/lib/copy/__tests__/ncp/ncp-error-perm.test.js b/lib/copy/__tests__/ncp/ncp-error-perm.test.js
new file mode 100644
index 0000000..5038959
--- /dev/null
+++ b/lib/copy/__tests__/ncp/ncp-error-perm.test.js
@@ -0,0 +1,50 @@
+// file in reference: https://github.com/jprichardson/node-fs-extra/issues/56
+
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+var ncp = require('../../ncp')
+
+/* global afterEach, beforeEach, describe, it */
+
+// skip test for windows
+// eslint-disable globalReturn */
+// if (os.platform().indexOf('win') === 0) return
+// eslint-enable globalReturn */
+
+describe('ncp / error / dest-permission', function () {
+  var TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'ncp-error-dest-perm')
+  var src = path.join(TEST_DIR, 'src')
+  var dest = path.join(TEST_DIR, 'dest')
+
+  if (os.platform().indexOf('win') === 0) return
+
+  beforeEach(function (done) {
+    fse.emptyDir(TEST_DIR, function (err) {
+      assert.ifError(err)
+      done()
+    })
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('should return an error', function (done) {
+    var someFile = path.join(src, 'some-file')
+    fse.outputFileSync(someFile, 'hello')
+
+    fse.mkdirsSync(dest)
+    fs.chmodSync(dest, parseInt('444', 8))
+
+    var subdest = path.join(dest, 'another-dir')
+
+    ncp(src, subdest, function (err) {
+      assert(err)
+      assert.equal(err.code, 'EACCES')
+      done()
+    })
+  })
+})
diff --git a/lib/copy/__tests__/ncp/ncp.test.js b/lib/copy/__tests__/ncp/ncp.test.js
new file mode 100644
index 0000000..e091306
--- /dev/null
+++ b/lib/copy/__tests__/ncp/ncp.test.js
@@ -0,0 +1,171 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var rimraf = require('rimraf')
+var readDirFiles = require('read-dir-files').read // temporary, will remove
+var ncp = require('../../ncp')
+
+/* eslint-env mocha */
+
+var fixturesDir = path.join(__dirname, 'fixtures')
+
+describe('ncp', function () {
+  describe('regular files and directories', function () {
+    var fixtures = path.join(fixturesDir, 'regular-fixtures')
+    var src = path.join(fixtures, 'src')
+    var out = path.join(fixtures, 'out')
+
+    before(function (cb) {
+      rimraf(out, function () {
+        ncp(src, out, cb)
+      })
+    })
+
+    describe('when copying a directory of files', function () {
+      it('files are copied correctly', function (cb) {
+        readDirFiles(src, 'utf8', function (srcErr, srcFiles) {
+          readDirFiles(out, 'utf8', function (outErr, outFiles) {
+            assert.ifError(srcErr)
+            assert.deepEqual(srcFiles, outFiles)
+            cb()
+          })
+        })
+      })
+    })
+
+    describe('when copying files using filter', function () {
+      before(function (cb) {
+        var filter = function (name) {
+          return name.substr(name.length - 1) !== 'a'
+        }
+        rimraf(out, function () {
+          ncp(src, out, {filter: filter}, cb)
+        })
+      })
+
+      it('files are copied correctly', function (cb) {
+        readDirFiles(src, 'utf8', function (srcErr, srcFiles) {
+          function filter (files) {
+            for (var fileName in files) {
+              var curFile = files[fileName]
+              if (curFile instanceof Object) {
+                return filter(curFile)
+              }
+
+              if (fileName.substr(fileName.length - 1) === 'a') {
+                delete files[fileName]
+              }
+            }
+          }
+          filter(srcFiles)
+          readDirFiles(out, 'utf8', function (outErr, outFiles) {
+            assert.ifError(outErr)
+            assert.deepEqual(srcFiles, outFiles)
+            cb()
+          })
+        })
+      })
+    })
+
+    describe('when using clobber=true', function () {
+      before(function () {
+        this.originalCreateReadStream = fs.createReadStream
+      })
+
+      after(function () {
+        fs.createReadStream = this.originalCreateReadStream
+      })
+
+      it('the copy is complete after callback', function (done) {
+        ncp(src, out, {clobber: true}, function (err) {
+          fs.createReadStream = function () {
+            done(new Error('createReadStream after callback'))
+          }
+          assert.ifError(err)
+          process.nextTick(done)
+        })
+      })
+    })
+
+    describe('when using clobber=false', function () {
+      beforeEach(function (done) {
+        rimraf(out, done)
+      })
+      it('works', function (cb) {
+        ncp(src, out, {clobber: false}, function (err) {
+          assert.ifError(err)
+          cb()
+        })
+      })
+      it('errors if files exist', function (cb) {
+        ncp(src, out, function () {
+          ncp(src, out, {clobber: false}, function (err) {
+            assert(err)
+            cb()
+          })
+        })
+      })
+    })
+
+    describe('when using transform', function () {
+      it('file descriptors are passed correctly', function (cb) {
+        ncp(src, out, {
+          transform: function (read, write, file) {
+            assert.notEqual(file.name, undefined)
+            assert.strictEqual(typeof file.mode, 'number')
+            read.pipe(write)
+          }
+        }, cb)
+      })
+    })
+  })
+
+  // see https://github.com/AvianFlu/ncp/issues/71
+  describe('Issue 71: Odd Async Behaviors', function (cb) {
+    var fixtures = path.join(__dirname, 'fixtures', 'regular-fixtures')
+    var src = path.join(fixtures, 'src')
+    var out = path.join(fixtures, 'out')
+
+    var totalCallbacks = 0
+
+    function copyAssertAndCount (callback) {
+      // rimraf(out, function() {
+      ncp(src, out, function (err) {
+        assert(!err)
+        totalCallbacks += 1
+        readDirFiles(src, 'utf8', function (srcErr, srcFiles) {
+          readDirFiles(out, 'utf8', function (outErr, outFiles) {
+            assert.ifError(srcErr)
+            assert.deepEqual(srcFiles, outFiles)
+            callback()
+          })
+        })
+      })
+      // })
+    }
+
+    describe('when copying a directory of files without cleaning the destination', function () {
+      it('callback fires once per run and directories are equal', function (done) {
+        var expected = 10
+        var count = 10
+
+        function next () {
+          if (count > 0) {
+            setTimeout(function () {
+              copyAssertAndCount(function () {
+                count -= 1
+                next()
+              })
+            }, 100)
+          } else {
+            // console.log('Total callback count is', totalCallbacks)
+            assert.equal(totalCallbacks, expected)
+            done()
+          }
+        }
+
+        next()
+      })
+    })
+  })
+})
diff --git a/lib/copy/__tests__/ncp/symlink.test.js b/lib/copy/__tests__/ncp/symlink.test.js
new file mode 100644
index 0000000..3315a1c
--- /dev/null
+++ b/lib/copy/__tests__/ncp/symlink.test.js
@@ -0,0 +1,78 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+var ncp = require('../../ncp')
+
+/* global afterEach, beforeEach, describe, it */
+
+describe('ncp / symlink', function () {
+  var TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'ncp-symlinks')
+  var src = path.join(TEST_DIR, 'src')
+  var out = path.join(TEST_DIR, 'out')
+
+  beforeEach(function (done) {
+    fse.emptyDir(TEST_DIR, function (err) {
+      assert.ifError(err)
+      createFixtures(src, done)
+    })
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('copies symlinks by default', function (done) {
+    ncp(src, out, function (err) {
+      assert.ifError(err)
+
+      assert.equal(fs.readlinkSync(path.join(out, 'file-symlink')), path.join(src, 'foo'))
+      assert.equal(fs.readlinkSync(path.join(out, 'dir-symlink')), path.join(src, 'dir'))
+
+      done()
+    })
+  })
+
+  it('copies file contents when dereference=true', function (done) {
+    ncp(src, out, {dereference: true}, function (err) {
+      assert.ifError(err)
+
+      var fileSymlinkPath = path.join(out, 'file-symlink')
+      assert.ok(fs.lstatSync(fileSymlinkPath).isFile())
+      assert.equal(fs.readFileSync(fileSymlinkPath), 'foo contents')
+
+      var dirSymlinkPath = path.join(out, 'dir-symlink')
+      assert.ok(fs.lstatSync(dirSymlinkPath).isDirectory())
+      assert.deepEqual(fs.readdirSync(dirSymlinkPath), ['bar'])
+
+      done()
+    })
+  })
+})
+
+function createFixtures (srcDir, callback) {
+  fs.mkdir(srcDir, function (err) {
+    if (err) return callback(err)
+
+    // note: third parameter in symlinkSync is type e.g. 'file' or 'dir'
+    // https://nodejs.org/api/fs.html#fs_fs_symlink_srcpath_dstpath_type_callback
+    try {
+      var fooFile = path.join(srcDir, 'foo')
+      var fooFileLink = path.join(srcDir, 'file-symlink')
+      fs.writeFileSync(fooFile, 'foo contents')
+      fs.symlinkSync(fooFile, fooFileLink, 'file')
+
+      var dir = path.join(srcDir, 'dir')
+      var dirFile = path.join(dir, 'bar')
+      var dirLink = path.join(srcDir, 'dir-symlink')
+      fs.mkdirSync(dir)
+      fs.writeFileSync(dirFile, 'bar contents')
+      fs.symlinkSync(dir, dirLink, 'dir')
+    } catch (err) {
+      callback(err)
+    }
+
+    callback()
+  })
+}
diff --git a/lib/copy/copy.js b/lib/copy/copy.js
new file mode 100644
index 0000000..6c5e224
--- /dev/null
+++ b/lib/copy/copy.js
@@ -0,0 +1,50 @@
+var fs = require('graceful-fs')
+var path = require('path')
+var ncp = require('./ncp')
+var mkdir = require('../mkdirs')
+
+function copy (src, dest, options, callback) {
+  if (typeof options === 'function' && !callback) {
+    callback = options
+    options = {}
+  } else if (typeof options === 'function' || options instanceof RegExp) {
+    options = {filter: options}
+  }
+  callback = callback || function () {}
+  options = options || {}
+
+  // Warn about using preserveTimestamps on 32-bit node:
+  if (options.preserveTimestamps && process.arch === 'ia32') {
+    console.warn('fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n' +
+    'see https://github.com/jprichardson/node-fs-extra/issues/269')
+  }
+
+  // don't allow src and dest to be the same
+  var basePath = process.cwd()
+  var currentPath = path.resolve(basePath, src)
+  var targetPath = path.resolve(basePath, dest)
+  if (currentPath === targetPath) return callback(new Error('Source and destination must not be the same.'))
+
+  fs.lstat(src, function (err, stats) {
+    if (err) return callback(err)
+
+    var dir = null
+    if (stats.isDirectory()) {
+      var parts = dest.split(path.sep)
+      parts.pop()
+      dir = parts.join(path.sep)
+    } else {
+      dir = path.dirname(dest)
+    }
+
+    fs.exists(dir, function (dirExists) {
+      if (dirExists) return ncp(src, dest, options, callback)
+      mkdir.mkdirs(dir, function (err) {
+        if (err) return callback(err)
+        ncp(src, dest, options, callback)
+      })
+    })
+  })
+}
+
+module.exports = copy
diff --git a/lib/copy/index.js b/lib/copy/index.js
new file mode 100644
index 0000000..3e09016
--- /dev/null
+++ b/lib/copy/index.js
@@ -0,0 +1,3 @@
+module.exports = {
+  copy: require('./copy')
+}
diff --git a/lib/_copy.js b/lib/copy/ncp.js
similarity index 51%
rename from lib/_copy.js
rename to lib/copy/ncp.js
index c42108e..a951004 100644
--- a/lib/_copy.js
+++ b/lib/copy/ncp.js
@@ -2,12 +2,11 @@
 
 var fs = require('graceful-fs')
 var path = require('path')
+var utimes = require('../util/utimes')
 
 function ncp (source, dest, options, callback) {
-  var cback = callback
-
   if (!callback) {
-    cback = options
+    callback = options
     options = {}
   }
 
@@ -17,121 +16,121 @@ function ncp (source, dest, options, callback) {
 
   var filter = options.filter
   var transform = options.transform
-  var clobber = options.clobber !== false
+  var clobber = options.clobber !== false // default true
   var dereference = options.dereference
+  var preserveTimestamps = options.preserveTimestamps === true
 
-  var errs = null
-  
   var started = 0
   var finished = 0
   var running = 0
-  // this is pretty useless now that we're using graceful-fs
-  // consider removing
-  var limit = options.limit || 512
+
+  var errored = false
 
   startCopy(currentPath)
-  
-  function startCopy(source) {
+
+  function startCopy (source) {
     started++
     if (filter) {
       if (filter instanceof RegExp) {
+        console.warn('Warning: fs-extra: Passing a RegExp filter is deprecated, use a function')
         if (!filter.test(source)) {
-          return cb(true)
+          return doneOne(true)
         }
-      }
-      else if (typeof filter === 'function') {
+      } else if (typeof filter === 'function') {
         if (!filter(source)) {
-          return cb(true)
+          return doneOne(true)
         }
       }
     }
     return getStats(source)
   }
 
-  function getStats(source) {
-    var defer = global.setImmediate || process.nextTick
+  function getStats (source) {
     var stat = dereference ? fs.stat : fs.lstat
-    if (running >= limit) {
-      return defer(function () {
-        getStats(source)
-      })
-    }
     running++
     stat(source, function (err, stats) {
-      var item = {}
-      if (err) {
-        return onError(err)
-      }
+      if (err) return onError(err)
 
       // We need to get the mode from the stats object and preserve it.
-      item.name = source
-      item.mode = stats.mode
-      item.mtime = stats.mtime //modified time
-      item.atime = stats.atime //access time
+      var item = {
+        name: source,
+        mode: stats.mode,
+        mtime: stats.mtime, // modified time
+        atime: stats.atime, // access time
+        stats: stats // temporary
+      }
 
       if (stats.isDirectory()) {
         return onDir(item)
-      }
-      else if (stats.isFile()) {
+      } else if (stats.isFile() || stats.isCharacterDevice() || stats.isBlockDevice()) {
         return onFile(item)
-      }
-      else if (stats.isSymbolicLink()) {
+      } else if (stats.isSymbolicLink()) {
         // Symlinks don't really need to know about the mode.
         return onLink(source)
       }
     })
   }
 
-  function onFile(file) {
-    var target = file.name.replace(currentPath, targetPath)
+  function onFile (file) {
+    var target = file.name.replace(currentPath, targetPath.replace('$', '$$$$')) // escapes '$' with '$$'
     isWritable(target, function (writable) {
       if (writable) {
         copyFile(file, target)
       } else {
-        if(clobber) {
+        if (clobber) {
           rmFile(target, function () {
             copyFile(file, target)
           })
         } else {
-          cb()
+          var err = new Error('EEXIST: ' + target + ' already exists.')
+          err.code = 'EEXIST'
+          err.errno = -17
+          err.path = target
+          onError(err)
         }
       }
     })
   }
 
-  function copyFile(file, target) {
-    var readStream = fs.createReadStream(file.name),
-        writeStream = fs.createWriteStream(target, { mode: file.mode })
-    
+  function copyFile (file, target) {
+    var readStream = fs.createReadStream(file.name)
+    var writeStream = fs.createWriteStream(target, { mode: file.mode })
+
     readStream.on('error', onError)
     writeStream.on('error', onError)
-    
-    if(transform) {
+
+    if (transform) {
       transform(readStream, writeStream, file)
     } else {
-      writeStream.on('open', function() {
+      writeStream.on('open', function () {
         readStream.pipe(writeStream)
       })
     }
 
-    //presumably old node then
-    var eventName = global.setImmediate ? 'finish' : 'close'
-    writeStream.once(eventName, function() {
-      cb()
+    writeStream.once('finish', function () {
+      fs.chmod(target, file.mode, function (err) {
+        if (err) return onError(err)
+        if (preserveTimestamps) {
+          utimes.utimesMillis(target, file.atime, file.mtime, function (err) {
+            if (err) return onError(err)
+            return doneOne()
+          })
+        } else {
+          doneOne()
+        }
+      })
     })
   }
 
-  function rmFile(file, done) {
+  function rmFile (file, done) {
     fs.unlink(file, function (err) {
-      if (err) {
-        return onError(err)
-      }
+      if (err) return onError(err)
       return done()
     })
   }
 
-  function onDir(dir) {
-    var target = dir.name.replace(currentPath, targetPath)
+  function onDir (dir) {
+    var target = dir.name.replace(currentPath, targetPath.replace('$', '$$$$')) // escapes '$' with '$$'
     isWritable(target, function (writable) {
       if (writable) {
         return mkDir(dir, target)
@@ -140,38 +139,37 @@ function ncp (source, dest, options, callback) {
     })
   }
 
-  function mkDir(dir, target) {
+  function mkDir (dir, target) {
     fs.mkdir(target, dir.mode, function (err) {
-      if (err) {
-        return onError(err)
-      }
-      copyDir(dir.name)
+      if (err) return onError(err)
+      // despite setting mode in fs.mkdir, doesn't seem to work
+      // so we set it here.
+      fs.chmod(target, dir.mode, function (err) {
+        if (err) return onError(err)
+        copyDir(dir.name)
+      })
     })
   }
 
-  function copyDir(dir) {
+  function copyDir (dir) {
     fs.readdir(dir, function (err, items) {
-      if (err) {
-        return onError(err)
-      }
+      if (err) return onError(err)
       items.forEach(function (item) {
         startCopy(path.join(dir, item))
       })
-      return cb()
+      return doneOne()
     })
   }
 
-  function onLink(link) {
+  function onLink (link) {
     var target = link.replace(currentPath, targetPath)
     fs.readlink(link, function (err, resolvedPath) {
-      if (err) {
-        return onError(err)
-      }
+      if (err) return onError(err)
       checkLink(resolvedPath, target)
     })
   }
 
-  function checkLink(resolvedPath, target) {
+  function checkLink (resolvedPath, target) {
     if (dereference) {
       resolvedPath = path.resolve(basePath, resolvedPath)
     }
@@ -180,14 +178,13 @@ function ncp (source, dest, options, callback) {
         return makeLink(resolvedPath, target)
       }
       fs.readlink(target, function (err, targetDest) {
-        if (err) {
-          return onError(err)
-        }
+        if (err) return onError(err)
+
         if (dereference) {
           targetDest = path.resolve(basePath, targetDest)
         }
         if (targetDest === resolvedPath) {
-          return cb()
+          return doneOne()
         }
         return rmFile(target, function () {
           makeLink(resolvedPath, target)
@@ -196,16 +193,14 @@ function ncp (source, dest, options, callback) {
     })
   }
 
-  function makeLink(linkPath, target) {
+  function makeLink (linkPath, target) {
     fs.symlink(linkPath, target, function (err) {
-      if (err) {
-        return onError(err)
-      }
-      return cb()
+      if (err) return onError(err)
+      return doneOne()
     })
   }
 
-  function isWritable(path, done) {
+  function isWritable (path, done) {
     fs.lstat(path, function (err) {
       if (err) {
         if (err.code === 'ENOENT') return done(true)
@@ -215,36 +210,23 @@ function ncp (source, dest, options, callback) {
     })
   }
 
-  function onError(err) {
-    if (options.stopOnError) {
-      return cback(err)
-    }
-    else if (!errs && options.errs) {
-      errs = fs.createWriteStream(options.errs)
+  function onError (err) {
+    // ensure callback is defined & called only once:
+    if (!errored && callback !== undefined) {
+      errored = true
+      return callback(err)
     }
-    else if (!errs) {
-      errs = []
-    }
-    if (typeof errs.write === 'undefined') {
-      errs.push(err)
-    }
-    else { 
-      errs.write(err.stack + '\n\n')
-    }
-    return cb()
   }
 
-  function cb(skipped) {
+  function doneOne (skipped) {
     if (!skipped) running--
     finished++
     if ((started === finished) && (running === 0)) {
-      if (cback !== undefined ) {
-        return errs ? cback(errs) : cback(null)
+      if (callback !== undefined) {
+        return callback(null)
       }
     }
   }
 }
 
-// todo, make this just export ncp
-module.exports.ncp = ncp
-
+module.exports = ncp
diff --git a/lib/empty/__tests__/empty-dir-sync.test.js b/lib/empty/__tests__/empty-dir-sync.test.js
new file mode 100644
index 0000000..3587472
--- /dev/null
+++ b/lib/empty/__tests__/empty-dir-sync.test.js
@@ -0,0 +1,54 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+
+/* global afterEach, beforeEach, describe, it */
+
+describe('+ emptyDir()', function () {
+  var TEST_DIR
+
+  beforeEach(function () {
+    TEST_DIR = path.join(os.tmpdir(), 'test-fs-extra', 'empty-dir')
+    if (fs.existsSync(TEST_DIR)) {
+      fse.removeSync(TEST_DIR)
+    }
+    fse.ensureDirSync(TEST_DIR)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  describe('> when directory exists and contains items', function () {
+    it('should delete all of the items', function () {
+      // verify nothing
+      assert.equal(fs.readdirSync(TEST_DIR).length, 0)
+      fse.ensureFileSync(path.join(TEST_DIR, 'some-file'))
+      fse.ensureFileSync(path.join(TEST_DIR, 'some-file-2'))
+      fse.ensureDirSync(path.join(TEST_DIR, 'some-dir'))
+      assert.equal(fs.readdirSync(TEST_DIR).length, 3)
+
+      fse.emptyDirSync(TEST_DIR)
+      assert.equal(fs.readdirSync(TEST_DIR).length, 0)
+    })
+  })
+
+  describe('> when directory exists and contains no items', function () {
+    it('should do nothing', function () {
+      assert.equal(fs.readdirSync(TEST_DIR).length, 0)
+      fse.emptyDirSync(TEST_DIR)
+      assert.equal(fs.readdirSync(TEST_DIR).length, 0)
+    })
+  })
+
+  describe('> when directory does not exist', function () {
+    it('should create it', function () {
+      fse.removeSync(TEST_DIR)
+      assert(!fs.existsSync(TEST_DIR))
+      fse.emptyDirSync(TEST_DIR)
+      assert.equal(fs.readdirSync(TEST_DIR).length, 0)
+    })
+  })
+})
diff --git a/lib/empty/__tests__/empty-dir.test.js b/lib/empty/__tests__/empty-dir.test.js
new file mode 100644
index 0000000..8ef1304
--- /dev/null
+++ b/lib/empty/__tests__/empty-dir.test.js
@@ -0,0 +1,63 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+
+/* global afterEach, beforeEach, describe, it */
+
+describe('+ emptyDir()', function () {
+  var TEST_DIR
+
+  beforeEach(function () {
+    TEST_DIR = path.join(os.tmpdir(), 'test-fs-extra', 'empty-dir')
+    if (fs.existsSync(TEST_DIR)) {
+      fse.removeSync(TEST_DIR)
+    }
+    fse.ensureDirSync(TEST_DIR)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  describe('> when directory exists and contains items', function () {
+    it('should delete all of the items', function (done) {
+      // verify nothing
+      assert.equal(fs.readdirSync(TEST_DIR).length, 0)
+      fse.ensureFileSync(path.join(TEST_DIR, 'some-file'))
+      fse.ensureFileSync(path.join(TEST_DIR, 'some-file-2'))
+      fse.ensureDirSync(path.join(TEST_DIR, 'some-dir'))
+      assert.equal(fs.readdirSync(TEST_DIR).length, 3)
+
+      fse.emptyDir(TEST_DIR, function (err) {
+        assert.ifError(err)
+        assert.equal(fs.readdirSync(TEST_DIR).length, 0)
+        done()
+      })
+    })
+  })
+
+  describe('> when directory exists and contains no items', function () {
+    it('should do nothing', function (done) {
+      assert.equal(fs.readdirSync(TEST_DIR).length, 0)
+      fse.emptyDir(TEST_DIR, function (err) {
+        assert.ifError(err)
+        assert.equal(fs.readdirSync(TEST_DIR).length, 0)
+        done()
+      })
+    })
+  })
+
+  describe('> when directory does not exist', function () {
+    it('should create it', function (done) {
+      fse.removeSync(TEST_DIR)
+      assert(!fs.existsSync(TEST_DIR))
+      fse.emptyDir(TEST_DIR, function (err) {
+        assert.ifError(err)
+        assert.equal(fs.readdirSync(TEST_DIR).length, 0)
+        done()
+      })
+    })
+  })
+})
diff --git a/lib/empty/index.js b/lib/empty/index.js
new file mode 100644
index 0000000..a17cbae
--- /dev/null
+++ b/lib/empty/index.js
@@ -0,0 +1,47 @@
+var fs = require('fs')
+var path = require('path')
+var mkdir = require('../mkdirs')
+var remove = require('../remove')
+
+function emptyDir (dir, callback) {
+  callback = callback || function () {}
+  fs.readdir(dir, function (err, items) {
+    if (err) return mkdir.mkdirs(dir, callback)
+
+    items = items.map(function (item) {
+      return path.join(dir, item)
+    })
+
+    deleteItem()
+
+    function deleteItem () {
+      var item = items.pop()
+      if (!item) return callback()
+      remove.remove(item, function (err) {
+        if (err) return callback(err)
+        deleteItem()
+      })
+    }
+  })
+}
+
+function emptyDirSync (dir) {
+  var items
+  try {
+    items = fs.readdirSync(dir)
+  } catch (err) {
+    return mkdir.mkdirsSync(dir)
+  }
+
+  items.forEach(function (item) {
+    item = path.join(dir, item)
+    remove.removeSync(item)
+  })
+}
+
+module.exports = {
+  emptyDirSync: emptyDirSync,
+  emptydirSync: emptyDirSync,
+  emptyDir: emptyDir,
+  emptydir: emptyDir
+}
diff --git a/test/create.test.js b/lib/ensure/__tests__/create.test.js
similarity index 62%
rename from test/create.test.js
rename to lib/ensure/__tests__/create.test.js
index 7c205b0..94ce3c1 100644
--- a/test/create.test.js
+++ b/lib/ensure/__tests__/create.test.js
@@ -1,26 +1,29 @@
 var assert = require('assert')
 var fs = require('fs')
 var path = require('path')
-var testutil = require('testutil')
-var fse = require('../')
+var os = require('os')
+var fse = require(process.cwd())
 
-var TEST_DIR = ''
+/* global afterEach, beforeEach, describe, it */
 
 describe('fs-extra', function () {
-  beforeEach(function() {
-    TEST_DIR = testutil.createTestDir('fs-extra')
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'create')
+    fse.emptyDir(TEST_DIR, done)
   })
 
-  afterEach(function(done) {
+  afterEach(function (done) {
     fse.remove(TEST_DIR, done)
   })
 
-  describe('+ createFile', function() {
-    describe('> when the file and directory does not exist', function() {
-      it('should create the file', function(done) {
+  describe('+ createFile', function () {
+    describe('> when the file and directory does not exist', function () {
+      it('should create the file', function (done) {
         var file = path.join(TEST_DIR, Math.random() + 't-ne', Math.random() + '.txt')
         assert(!fs.existsSync(file))
-        fse.createFile(file, function(err) {
+        fse.createFile(file, function (err) {
           assert.ifError(err)
           assert(fs.existsSync(file))
           done()
@@ -28,12 +31,12 @@ describe('fs-extra', function () {
       })
     })
 
-    describe('> when the file does exist', function() {
-      it('should not modify the file', function(done) {
+    describe('> when the file does exist', function () {
+      it('should not modify the file', function (done) {
         var file = path.join(TEST_DIR, Math.random() + 't-e', Math.random() + '.txt')
         fse.mkdirsSync(path.dirname(file))
         fs.writeFileSync(file, 'hello world')
-        fse.createFile(file, function(err) {
+        fse.createFile(file, function (err) {
           assert.ifError(err)
           assert.equal(fs.readFileSync(file, 'utf8'), 'hello world')
           done()
@@ -42,9 +45,9 @@ describe('fs-extra', function () {
     })
   })
 
-  describe('+ createFileSync', function() {
-    describe('> when the file and directory does not exist', function() {
-      it('should create the file', function() {
+  describe('+ createFileSync', function () {
+    describe('> when the file and directory does not exist', function () {
+      it('should create the file', function () {
         var file = path.join(TEST_DIR, Math.random() + 'ts-ne', Math.random() + '.txt')
         assert(!fs.existsSync(file))
         fse.createFileSync(file)
@@ -52,8 +55,8 @@ describe('fs-extra', function () {
       })
     })
 
-    describe('> when the file does exist', function() {
-      it('should not modify the file', function() {
+    describe('> when the file does exist', function () {
+      it('should not modify the file', function () {
         var file = path.join(TEST_DIR, Math.random() + 'ts-e', Math.random() + '.txt')
         fse.mkdirsSync(path.dirname(file))
         fs.writeFileSync(file, 'hello world')
diff --git a/test/ensure.test.js b/lib/ensure/__tests__/ensure.test.js
similarity index 55%
rename from test/ensure.test.js
rename to lib/ensure/__tests__/ensure.test.js
index bf3c85d..5ff340f 100644
--- a/test/ensure.test.js
+++ b/lib/ensure/__tests__/ensure.test.js
@@ -1,28 +1,31 @@
 var assert = require('assert')
 var fs = require('fs')
 var path = require('path')
-var testutil = require('testutil')
-var fse = require('../')
+var os = require('os')
+var fse = require(process.cwd())
 
-var TEST_DIR = ''
+/* global afterEach, beforeEach, describe, it */
 
-describe('fs-extra', function() {
-  beforeEach(function() {
-    TEST_DIR = testutil.createTestDir('fs-extra')
+describe('fs-extra', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'ensure')
+    fse.emptyDir(TEST_DIR, done)
   })
 
-  afterEach(function(done) {
+  afterEach(function (done) {
     fse.remove(TEST_DIR, done)
   })
 
-  describe('+ ensureFile()', function() {
-    describe('> when file exists', function() {
-      it('should not do anything', function(done) {
+  describe('+ ensureFile()', function () {
+    describe('> when file exists', function () {
+      it('should not do anything', function (done) {
         var file = path.join(TEST_DIR, 'file.txt')
         fs.writeFileSync(file, 'blah')
 
         assert(fs.existsSync(file))
-        fse.ensureFile(file, function(err) {
+        fse.ensureFile(file, function (err) {
           assert.ifError(err)
           assert(fs.existsSync(file))
           done()
@@ -30,12 +33,12 @@ describe('fs-extra', function() {
       })
     })
 
-    describe('> when file does not exist', function() {
-      it('should create the file', function(done) {
+    describe('> when file does not exist', function () {
+      it('should create the file', function (done) {
         var file = path.join(TEST_DIR, 'dir/that/does/not/exist', 'file.txt')
 
         assert(!fs.existsSync(file))
-        fse.ensureFile(file, function(err) {
+        fse.ensureFile(file, function (err) {
           assert.ifError(err)
           assert(fs.existsSync(file))
           done()
@@ -44,9 +47,9 @@ describe('fs-extra', function() {
     })
   })
 
-  describe('+ ensureFileSync()', function() {
-    describe('> when file exists', function() {
-      it('should not do anything', function() {
+  describe('+ ensureFileSync()', function () {
+    describe('> when file exists', function () {
+      it('should not do anything', function () {
         var file = path.join(TEST_DIR, 'file.txt')
         fs.writeFileSync(file, 'blah')
 
@@ -56,8 +59,8 @@ describe('fs-extra', function() {
       })
     })
 
-    describe('> when file does not exist', function() {
-      it('should create the file', function() {
+    describe('> when file does not exist', function () {
+      it('should create the file', function () {
         var file = path.join(TEST_DIR, 'dir/that/does/not/exist', 'file.txt')
 
         assert(!fs.existsSync(file))
@@ -67,14 +70,14 @@ describe('fs-extra', function() {
     })
   })
 
-  describe('+ ensureDir()', function() {
-    describe('> when dir exists', function() {
-      it('should not do anything', function(done) {
+  describe('+ ensureDir()', function () {
+    describe('> when dir exists', function () {
+      it('should not do anything', function (done) {
         var dir = path.join(TEST_DIR, 'dir/does/not/exist')
         fse.mkdirpSync(dir)
 
         assert(fs.existsSync(dir))
-        fse.ensureDir(dir, function(err) {
+        fse.ensureDir(dir, function (err) {
           assert.ifError(err)
           assert(fs.existsSync(dir))
           done()
@@ -82,12 +85,12 @@ describe('fs-extra', function() {
       })
     })
 
-    describe('> when dir does not exist', function() {
-      it('should create the dir', function(done) {
+    describe('> when dir does not exist', function () {
+      it('should create the dir', function (done) {
         var dir = path.join(TEST_DIR, 'dir/that/does/not/exist')
 
         assert(!fs.existsSync(dir))
-        fse.ensureDir(dir, function(err) {
+        fse.ensureDir(dir, function (err) {
           assert.ifError(err)
           assert(fs.existsSync(dir))
           done()
@@ -96,9 +99,9 @@ describe('fs-extra', function() {
     })
   })
 
-  describe('+ ensureDirSync()', function() {
-    describe('> when dir exists', function() {
-      it('should not do anything', function() {
+  describe('+ ensureDirSync()', function () {
+    describe('> when dir exists', function () {
+      it('should not do anything', function () {
         var dir = path.join(TEST_DIR, 'dir/does/not/exist')
         fse.mkdirpSync(dir)
 
@@ -108,8 +111,8 @@ describe('fs-extra', function() {
       })
     })
 
-    describe('> when dir does not exist', function() {
-      it('should create the dir', function() {
+    describe('> when dir does not exist', function () {
+      it('should create the dir', function () {
         var dir = path.join(TEST_DIR, 'dir/that/does/not/exist')
 
         assert(!fs.existsSync(dir))
@@ -119,4 +122,3 @@ describe('fs-extra', function() {
     })
   })
 })
-
diff --git a/lib/ensure/__tests__/link.test.js b/lib/ensure/__tests__/link.test.js
new file mode 100644
index 0000000..f06f4e8
--- /dev/null
+++ b/lib/ensure/__tests__/link.test.js
@@ -0,0 +1,223 @@
+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 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'],
+    [['./missing.txt', './missing-dir/link.txt'], 'file-error', 'file-error'],
+    [['./foo.txt', './link.txt'], 'file-success', 'file-success'],
+    [['./dir-foo/foo.txt', './link.txt'], 'file-success', 'file-success'],
+    [['./missing.txt', './link.txt'], 'file-error', 'file-error'],
+    [['../foo.txt', './link.txt'], 'file-error', 'file-error'],
+    [['../dir-foo/foo.txt', './link.txt'], 'file-error', 'file-error'],
+    // error is thrown if destination path exists
+    [['./foo.txt', './dir-foo/foo.txt'], 'file-error', 'file-dest-exists'],
+    [[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 srcContent = fs.readFileSync(srcpath, 'utf8')
+        var dstDir = path.dirname(dstpath)
+        var dstBasename = path.basename(dstpath)
+        var isSymlink = fs.lstatSync(dstpath).isFile()
+        var dstContent = fs.readFileSync(dstpath, 'utf8')
+        var dstDirContents = fs.readdirSync(dstDir)
+        assert.equal(isSymlink, true)
+        assert.equal(srcContent, dstContent)
+        assert(dstDirContents.indexOf(dstBasename) >= 0)
+        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 dstdirExistsBefore = fs.existsSync(path.dirname(dstpath))
+      function callback (err) {
+        assert.equal(err instanceof Error, true)
+        // ensure that directories aren't created if there's an error
+        var dstdirExistsAfter = fs.existsSync(path.dirname(dstpath))
+        assert.equal(dstdirExistsBefore, dstdirExistsAfter)
+        return done()
+      }
+      args.push(callback)
+      return fn.apply(null, args)
+    })
+  }
+
+  function fileDestExists (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should do nothing using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function (done) {
+      var destinationContentBefore = fs.readFileSync(dstpath, 'utf8')
+      var callback = function (err) {
+        if (err) return done(err)
+        var destinationContentAfter = fs.readFileSync(dstpath, 'utf8')
+        assert.equal(destinationContentBefore, destinationContentAfter)
+        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 srcContent = fs.readFileSync(srcpath, 'utf8')
+      var dstDir = path.dirname(dstpath)
+      var dstBasename = path.basename(dstpath)
+      var isSymlink = fs.lstatSync(dstpath).isFile()
+      var dstContent = fs.readFileSync(dstpath, 'utf8')
+      var dstDirContents = fs.readdirSync(dstDir)
+      assert.equal(isSymlink, true)
+      assert.equal(srcContent, dstContent)
+      assert(dstDirContents.indexOf(dstBasename) >= 0)
+    })
+  }
+
+  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 () {
+      // will fail if dstdir is created and there's an error
+      var dstdirExistsBefore = fs.existsSync(path.dirname(dstpath))
+      var err = null
+      try {
+        fn.apply(null, args)
+      } catch (e) {
+        err = e
+      }
+      assert.equal(err instanceof Error, true)
+      var dstdirExistsAfter = fs.existsSync(path.dirname(dstpath))
+      assert.equal(dstdirExistsBefore, dstdirExistsAfter)
+    })
+  }
+
+  function fileDestExistsSync (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should do nothing using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function () {
+      var destinationContentBefore = fs.readFileSync(dstpath, 'utf8')
+      fn.apply(null, args)
+      var destinationContentAfter = fs.readFileSync(dstpath, 'utf8')
+      assert.equal(destinationContentBefore, destinationContentAfter)
+    })
+  }
+
+  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)
+      if (nativeBehavior === 'file-dest-exists') fileDestExists(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)
+      if (newBehavior === 'file-dest-exists') fileDestExists(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)
+      if (nativeBehavior === 'file-dest-exists') fileDestExists(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)
+      if (newBehavior === 'file-dest-exists') fileDestExistsSync(args, fn)
+    })
+  })
+})
diff --git a/lib/ensure/__tests__/symlink-paths.test.js b/lib/ensure/__tests__/symlink-paths.test.js
new file mode 100644
index 0000000..8bfaa38
--- /dev/null
+++ b/lib/ensure/__tests__/symlink-paths.test.js
@@ -0,0 +1,91 @@
+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)
+      })
+    })
+  })
+})
diff --git a/lib/ensure/__tests__/symlink-type.test.js b/lib/ensure/__tests__/symlink-type.test.js
new file mode 100644
index 0000000..e520863
--- /dev/null
+++ b/lib/ensure/__tests__/symlink-type.test.js
@@ -0,0 +1,122 @@
+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 _symlinkType = require('../symlink-type')
+var symlinkType = _symlinkType.symlinkType
+var symlinkTypeSync = _symlinkType.symlinkTypeSync
+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 = {
+    success: [
+      // [{arguments} [srcpath, dirpath, [type] , result]
+      // smart file type checking
+      [['./foo.txt'], 'file'],
+      [['./empty-dir'], 'dir'],
+      [['./dir-foo/foo.txt'], 'file'],
+      [['./dir-bar'], 'dir'],
+      [['./dir-bar/bar.txt'], 'file'],
+      [['./real-alpha/real-beta/real-gamma'], 'dir'],
+      // force dir
+      [['./foo.txt', 'dir'], 'dir'],
+      [['./empty-dir', 'dir'], 'dir'],
+      [['./dir-foo/foo.txt', 'dir'], 'dir'],
+      [['./dir-bar', 'dir'], 'dir'],
+      [['./dir-bar/bar.txt', 'dir'], 'dir'],
+      [['./real-alpha/real-beta/real-gamma', 'dir'], 'dir'],
+      // force file
+      [['./foo.txt', 'file'], 'file'],
+      [['./empty-dir', 'file'], 'file'],
+      [['./dir-foo/foo.txt', 'file'], 'file'],
+      [['./dir-bar', 'file'], 'file'],
+      [['./dir-bar/bar.txt', 'file'], 'file'],
+      [['./real-alpha/real-beta/real-gamma', 'file'], 'file'],
+      // default for files or dirs that don't exist is file
+      [['./missing.txt'], 'file'],
+      [['./missing'], 'file'],
+      [['./missing.txt'], 'file'],
+      [['./missing'], 'file'],
+      [['./empty-dir/missing.txt'], 'file'],
+      [['./empty-dir/missing'], 'file'],
+      [['./empty-dir/missing.txt'], 'file'],
+      [['./empty-dir/missing'], 'file'],
+      // when src doesnt exist and provided type 'file'
+      [['./missing.txt', 'file'], 'file'],
+      [['./missing', 'file'], 'file'],
+      [['./missing.txt', 'file'], 'file'],
+      [['./missing', 'file'], 'file'],
+      [['./empty-dir/missing.txt', 'file'], 'file'],
+      [['./empty-dir/missing', 'file'], 'file'],
+      [['./empty-dir/missing.txt', 'file'], 'file'],
+      [['./empty-dir/missing', 'file'], 'file'],
+      // when src doesnt exist and provided type 'dir'
+      [['./missing.txt', 'dir'], 'dir'],
+      [['./missing', 'dir'], 'dir'],
+      [['./missing.txt', 'dir'], 'dir'],
+      [['./missing', 'dir'], 'dir'],
+      [['./empty-dir/missing.txt', 'dir'], 'dir'],
+      [['./empty-dir/missing', 'dir'], 'dir'],
+      [['./empty-dir/missing.txt', 'dir'], 'dir'],
+      [['./empty-dir/missing', 'dir'], 'dir']
+    ]
+  }
+
+  describe('symlinkType()', function () {
+    tests.success.forEach(function (test) {
+      var args = test[0].slice(0)
+      var expectedType = test[1]
+      var should = util.format('should return \'%s\' when src \'%s\'', expectedType, args[0])
+      it(should, function (done) {
+        var callback = function (err, type) {
+          if (err) done(err)
+          assert.equal(type, expectedType)
+          done()
+        }
+        args.push(callback)
+        return symlinkType.apply(null, args)
+      })
+    })
+  })
+
+  describe('symlinkTypeSync()', function () {
+    tests.success.forEach(function (test) {
+      var args = test[0]
+      var expectedType = test[1]
+      var should = util.format('should return \'%s\' when src \'%s\'', expectedType, args[0])
+      it(should, function () {
+        var type = symlinkTypeSync.apply(null, args)
+        assert.equal(type, expectedType)
+      })
+    })
+  })
+})
diff --git a/lib/ensure/__tests__/symlink.test.js b/lib/ensure/__tests__/symlink.test.js
new file mode 100644
index 0000000..2e688ac
--- /dev/null
+++ b/lib/ensure/__tests__/symlink.test.js
@@ -0,0 +1,450 @@
+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 symlinkPathsSync = _symlinkPaths.symlinkPathsSync
+
+var ensureSymlink = fse.ensureSymlink
+var ensureSymlinkSync = fse.ensureSymlinkSync
+
+/* global afterEach, beforeEach, describe, it, after, before */
+
+var TEST_DIR
+
+describe('fse-ensure-symlink', function () {
+  TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'ensure-symlink')
+
+  var tests = [
+    // [[srcpath, dstpath], fs.symlink expect, fse.ensureSymlink expect]
+    [['./foo.txt', './symlink.txt'], 'file-success', 'file-success'],
+    [['../foo.txt', './empty-dir/symlink.txt'], 'file-success', 'file-success'],
+    [['../foo.txt', './empty-dir/symlink.txt'], 'file-success', 'file-success'],
+    [['./foo.txt', './dir-foo/symlink.txt'], 'file-success', 'file-success'],
+    [['./foo.txt', './empty-dir/symlink.txt'], 'file-broken', 'file-success'],
+    [['./foo.txt', './real-alpha/symlink.txt'], 'file-broken', 'file-success'],
+    [['./foo.txt', './real-alpha/real-beta/symlink.txt'], 'file-broken', 'file-success'],
+    [['./foo.txt', './real-alpha/real-beta/real-gamma/symlink.txt'], 'file-broken', 'file-success'],
+    [['./foo.txt', './alpha/symlink.txt'], 'file-error', 'file-success'],
+    [['./foo.txt', './alpha/beta/symlink.txt'], 'file-error', 'file-success'],
+    [['./foo.txt', './alpha/beta/gamma/symlink.txt'], 'file-error', 'file-success'],
+    [['./missing.txt', './symlink.txt'], 'file-broken', 'file-error'],
+    [['./missing.txt', './missing-dir/symlink.txt'], 'file-error', 'file-error'],
+    // error is thrown if destination path exists
+    [['./foo.txt', './dir-foo/foo.txt'], 'file-error', 'file-dest-exists'],
+    [['./dir-foo', './symlink-dir-foo'], 'dir-success', 'dir-success'],
+    [['../dir-bar', './dir-foo/symlink-dir-bar'], 'dir-success', 'dir-success'],
+    [['./dir-bar', './dir-foo/symlink-dir-bar'], 'dir-broken', 'dir-success'],
+    [['./dir-bar', './empty-dir/symlink-dir-bar'], 'dir-broken', 'dir-success'],
+    [['./dir-bar', './real-alpha/symlink-dir-bar'], 'dir-broken', 'dir-success'],
+    [['./dir-bar', './real-alpha/real-beta/symlink-dir-bar'], 'dir-broken', 'dir-success'],
+    [['./dir-bar', './real-alpha/real-beta/real-gamma/symlink-dir-bar'], 'dir-broken', 'dir-success'],
+    [['./dir-foo', './alpha/dir-foo'], 'dir-error', 'dir-success'],
+    [['./dir-foo', './alpha/beta/dir-foo'], 'dir-error', 'dir-success'],
+    [['./dir-foo', './alpha/beta/gamma/dir-foo'], 'dir-error', 'dir-success'],
+    [['./missing', './dir-foo/symlink-dir-missing'], 'dir-broken', 'dir-error'],
+    // error is thrown if destination path exists
+    [['./dir-foo', './real-alpha/real-beta'], 'dir-error', 'dir-dest-exists'],
+    [[path.resolve(path.join(TEST_DIR, './foo.txt')), './symlink.txt'], 'file-success', 'file-success'],
+    [[path.resolve(path.join(TEST_DIR, './dir-foo/foo.txt')), './symlink.txt'], 'file-success', 'file-success'],
+    [[path.resolve(path.join(TEST_DIR, './missing.txt')), './symlink.txt'], 'file-broken', 'file-error'],
+    [[path.resolve(path.join(TEST_DIR, '../foo.txt')), './symlink.txt'], 'file-broken', 'file-error'],
+    [[path.resolve(path.join(TEST_DIR, '../dir-foo/foo.txt')), './symlink.txt'], 'file-broken', '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 symlink file using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function (done) {
+      var callback = function (err) {
+        if (err) return done(err)
+        var relative = symlinkPathsSync(srcpath, dstpath)
+        var srcContent = fs.readFileSync(relative.toCwd, 'utf8')
+        var dstDir = path.dirname(dstpath)
+        var dstBasename = path.basename(dstpath)
+        var isSymlink = fs.lstatSync(dstpath).isSymbolicLink()
+        var dstContent = fs.readFileSync(dstpath, 'utf8')
+        var dstDirContents = fs.readdirSync(dstDir)
+        assert.equal(isSymlink, true)
+        assert.equal(srcContent, dstContent)
+        assert(dstDirContents.indexOf(dstBasename) >= 0)
+        return done()
+      }
+      args.push(callback)
+      return fn.apply(null, args)
+    })
+  }
+
+  function fileBroken (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should create broken symlink file using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function (done) {
+      var callback = function (err) {
+        if (err) return done(err)
+        var dstDir = path.dirname(dstpath)
+        var dstBasename = path.basename(dstpath)
+        var isSymlink = fs.lstatSync(dstpath).isSymbolicLink()
+        var dstDirContents = fs.readdirSync(dstDir)
+        assert.equal(isSymlink, true)
+        assert(dstDirContents.indexOf(dstBasename) >= 0)
+        assert.throws(function () {
+          return fs.readFileSync(dstpath, 'utf8')
+        }, Error)
+        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 symlink file using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function (done) {
+      var dstdirExistsBefore = fs.existsSync(path.dirname(dstpath))
+      function callback (err) {
+        assert.equal(err instanceof Error, true)
+        // ensure that directories aren't created if there's an error
+        var dstdirExistsAfter = fs.existsSync(path.dirname(dstpath))
+        assert.equal(dstdirExistsBefore, dstdirExistsAfter)
+        return done()
+      }
+      args.push(callback)
+      return fn.apply(null, args)
+    })
+  }
+
+  function fileDestExists (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should do nothing using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function (done) {
+      var destinationContentBefore = fs.readFileSync(dstpath, 'utf8')
+      var callback = function (err) {
+        if (err) return done(err)
+        var destinationContentAfter = fs.readFileSync(dstpath, 'utf8')
+        assert.equal(destinationContentBefore, destinationContentAfter)
+        return done()
+      }
+      args.push(callback)
+      return fn.apply(null, args)
+    })
+  }
+
+  function dirSuccess (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should create symlink dir using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function (done) {
+      var callback = function (err) {
+        if (err) return done(err)
+        var relative = symlinkPathsSync(srcpath, dstpath)
+        var srcContents = fs.readdirSync(relative.toCwd)
+        var dstDir = path.dirname(dstpath)
+        var dstBasename = path.basename(dstpath)
+        var isSymlink = fs.lstatSync(dstpath).isSymbolicLink()
+        var dstContents = fs.readdirSync(dstpath)
+        var dstDirContents = fs.readdirSync(dstDir)
+        assert.equal(isSymlink, true)
+        assert.deepEqual(srcContents, dstContents)
+        assert(dstDirContents.indexOf(dstBasename) >= 0)
+        return done()
+      }
+      args.push(callback)
+      return fn.apply(null, args)
+    })
+  }
+
+  function dirBroken (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should create broken symlink dir using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function (done) {
+      var callback = function (err) {
+        if (err) return done(err)
+        var dstDir = path.dirname(dstpath)
+        var dstBasename = path.basename(dstpath)
+        var isSymlink = fs.lstatSync(dstpath).isSymbolicLink()
+        var dstDirContents = fs.readdirSync(dstDir)
+        assert.equal(isSymlink, true)
+        assert(dstDirContents.indexOf(dstBasename) >= 0)
+        assert.throws(function () {
+          return fs.readdirSync(dstpath)
+        }, Error)
+        return done()
+      }
+      args.push(callback)
+      return fn.apply(null, args)
+    })
+  }
+
+  function dirError (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should return error when creating symlink dir using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function (done) {
+      var dstdirExistsBefore = fs.existsSync(path.dirname(dstpath))
+      function callback (err) {
+        assert.equal(err instanceof Error, true)
+        // ensure that directories aren't created if there's an error
+        var dstdirExistsAfter = fs.existsSync(path.dirname(dstpath))
+        assert.equal(dstdirExistsBefore, dstdirExistsAfter)
+        return done()
+      }
+      args.push(callback)
+      return fn.apply(null, args)
+    })
+  }
+
+  function dirDestExists (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should do nothing using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function (done) {
+      var destinationContentBefore = fs.readdirSync(dstpath)
+      var callback = function (err) {
+        if (err) return done(err)
+        var destinationContentAfter = fs.readdirSync(dstpath)
+        assert.deepEqual(destinationContentBefore, destinationContentAfter)
+        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 symlink file using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function () {
+      fn.apply(null, args)
+      var relative = symlinkPathsSync(srcpath, dstpath)
+      var srcContent = fs.readFileSync(relative.toCwd, 'utf8')
+      var dstDir = path.dirname(dstpath)
+      var dstBasename = path.basename(dstpath)
+      var isSymlink = fs.lstatSync(dstpath).isSymbolicLink()
+      var dstContent = fs.readFileSync(dstpath, 'utf8')
+      var dstDirContents = fs.readdirSync(dstDir)
+      assert.equal(isSymlink, true)
+      assert.equal(srcContent, dstContent)
+      assert(dstDirContents.indexOf(dstBasename) >= 0)
+    })
+  }
+
+  function fileBrokenSync (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should create broken symlink file using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function () {
+      fn.apply(null, args)
+      var dstDir = path.dirname(dstpath)
+      var dstBasename = path.basename(dstpath)
+      var isSymlink = fs.lstatSync(dstpath).isSymbolicLink()
+      var dstDirContents = fs.readdirSync(dstDir)
+      assert.equal(isSymlink, true)
+      assert(dstDirContents.indexOf(dstBasename) >= 0)
+      assert.throws(function () {
+        return fs.readFileSync(dstpath, 'utf8')
+      }, Error)
+    })
+  }
+
+  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 () {
+      var dstdirExistsBefore = fs.existsSync(path.dirname(dstpath))
+      var err = null
+      try {
+        fn.apply(null, args)
+      } catch (e) {
+        err = e
+      }
+      assert.equal(err instanceof Error, true)
+      var dstdirExistsAfter = fs.existsSync(path.dirname(dstpath))
+      assert.equal(dstdirExistsBefore, dstdirExistsAfter)
+    })
+  }
+
+  function fileDestExistsSync (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should do nothing using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function () {
+      var destinationContentBefore = fs.readFileSync(dstpath, 'utf8')
+      fn.apply(null, args)
+      var destinationContentAfter = fs.readFileSync(dstpath, 'utf8')
+      assert.equal(destinationContentBefore, destinationContentAfter)
+    })
+  }
+
+  function dirSuccessSync (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should create symlink dir using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function () {
+      fn.apply(null, args)
+      var relative = symlinkPathsSync(srcpath, dstpath)
+      var srcContents = fs.readdirSync(relative.toCwd)
+      var dstDir = path.dirname(dstpath)
+      var dstBasename = path.basename(dstpath)
+      var isSymlink = fs.lstatSync(dstpath).isSymbolicLink()
+      var dstContents = fs.readdirSync(dstpath)
+      var dstDirContents = fs.readdirSync(dstDir)
+      assert.equal(isSymlink, true)
+      assert.deepEqual(srcContents, dstContents)
+      assert(dstDirContents.indexOf(dstBasename) >= 0)
+    })
+  }
+
+  function dirBrokenSync (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should create broken symlink dir using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function () {
+      fn.apply(null, args)
+      var dstDir = path.dirname(dstpath)
+      var dstBasename = path.basename(dstpath)
+      var isSymlink = fs.lstatSync(dstpath).isSymbolicLink()
+      var dstDirContents = fs.readdirSync(dstDir)
+      assert.equal(isSymlink, true)
+      assert(dstDirContents.indexOf(dstBasename) >= 0)
+      assert.throws(function () {
+        return fs.readdirSync(dstpath)
+      }, Error)
+    })
+  }
+
+  function dirErrorSync (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should throw error when creating symlink dir using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function () {
+      var dstdirExistsBefore = fs.existsSync(path.dirname(dstpath))
+      var err = null
+      try {
+        fn.apply(null, args)
+      } catch (e) {
+        err = e
+      }
+      assert.equal(err instanceof Error, true)
+      var dstdirExistsAfter = fs.existsSync(path.dirname(dstpath))
+      assert.equal(dstdirExistsBefore, dstdirExistsAfter)
+    })
+  }
+
+  function dirDestExistsSync (args, fn) {
+    var srcpath = args[0]
+    var dstpath = args[1]
+    var should = util.format('should do nothing using src `%s` and dst `%s`', srcpath, dstpath)
+    it(should, function () {
+      var destinationContentBefore = fs.readdirSync(dstpath)
+      fn.apply(null, args)
+      var destinationContentAfter = fs.readdirSync(dstpath)
+      assert.deepEqual(destinationContentBefore, destinationContentAfter)
+    })
+  }
+
+  describe('fs.symlink()', function () {
+    var fn = fs.symlink
+    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-broken') fileBroken(args, fn)
+      if (nativeBehavior === 'file-error') fileError(args, fn)
+      if (nativeBehavior === 'file-dest-exists') fileDestExists(args, fn)
+      args.push('dir')
+      if (nativeBehavior === 'dir-success') dirSuccess(args, fn)
+      if (nativeBehavior === 'dir-broken') dirBroken(args, fn)
+      if (nativeBehavior === 'dir-error') dirError(args, fn)
+      if (nativeBehavior === 'dir-dest-exists') dirDestExists(args, fn)
+    })
+  })
+
+  describe('ensureSymlink()', function () {
+    var fn = ensureSymlink
+    tests.forEach(function (test) {
+      var args = test[0]
+      // var nativeBehavior = test[1]
+      var newBehavior = test[2]
+      if (newBehavior === 'file-success') fileSuccess(args, fn)
+      if (newBehavior === 'file-broken') fileBroken(args, fn)
+      if (newBehavior === 'file-error') fileError(args, fn)
+      if (newBehavior === 'file-dest-exists') fileDestExists(args, fn)
+      if (newBehavior === 'dir-success') dirSuccess(args, fn)
+      if (newBehavior === 'dir-broken') dirBroken(args, fn)
+      if (newBehavior === 'dir-error') dirError(args, fn)
+      if (newBehavior === 'dir-dest-exists') dirDestExists(args, fn)
+    })
+  })
+
+  describe('fs.symlinkSync()', function () {
+    var fn = fs.symlinkSync
+    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-broken') fileBrokenSync(args, fn)
+      if (nativeBehavior === 'file-error') fileErrorSync(args, fn)
+      if (nativeBehavior === 'file-dest-exists') fileDestExistsSync(args, fn)
+      args.push('dir')
+      if (nativeBehavior === 'dir-success') dirSuccessSync(args, fn)
+      if (nativeBehavior === 'dir-broken') dirBrokenSync(args, fn)
+      if (nativeBehavior === 'dir-error') dirErrorSync(args, fn)
+      if (nativeBehavior === 'dir-dest-exists') dirDestExistsSync(args, fn)
+    })
+  })
+
+  describe('ensureSymlinkSync()', function () {
+    var fn = ensureSymlinkSync
+    tests.forEach(function (test) {
+      var args = test[0]
+      // var nativeBehavior = test[1]
+      var newBehavior = test[2]
+      if (newBehavior === 'file-success') fileSuccessSync(args, fn)
+      if (newBehavior === 'file-broken') fileBrokenSync(args, fn)
+      if (newBehavior === 'file-error') fileErrorSync(args, fn)
+      if (newBehavior === 'file-dest-exists') fileDestExistsSync(args, fn)
+      if (newBehavior === 'dir-success') dirSuccessSync(args, fn)
+      if (newBehavior === 'dir-broken') dirBrokenSync(args, fn)
+      if (newBehavior === 'dir-error') dirErrorSync(args, fn)
+      if (newBehavior === 'dir-dest-exists') dirDestExistsSync(args, fn)
+    })
+  })
+})
diff --git a/lib/create.js b/lib/ensure/file.js
similarity index 60%
rename from lib/create.js
rename to lib/ensure/file.js
index 68e9582..1c9c2de 100644
--- a/lib/create.js
+++ b/lib/ensure/file.js
@@ -1,21 +1,21 @@
 var path = require('path')
 var fs = require('graceful-fs')
-var mkdir = require('./mkdir')
+var mkdir = require('../mkdirs')
 
 function createFile (file, callback) {
-  function makeFile() {
-    fs.writeFile(file, '', function(err) {
+  function makeFile () {
+    fs.writeFile(file, '', function (err) {
       if (err) return callback(err)
       callback()
     })
   }
 
-  fs.exists(file, function(fileExists) {
+  fs.exists(file, function (fileExists) {
     if (fileExists) return callback()
     var dir = path.dirname(file)
-    fs.exists(dir, function(dirExists) {
+    fs.exists(dir, function (dirExists) {
       if (dirExists) return makeFile()
-      mkdir.mkdirs(dir, function(err) {
+      mkdir.mkdirs(dir, function (err) {
         if (err) return callback(err)
         makeFile()
       })
@@ -27,13 +27,17 @@ function createFileSync (file) {
   if (fs.existsSync(file)) return
 
   var dir = path.dirname(file)
-  if (!fs.existsSync(dir))
+  if (!fs.existsSync(dir)) {
     mkdir.mkdirsSync(dir)
+  }
 
   fs.writeFileSync(file, '')
 }
 
 module.exports = {
   createFile: createFile,
-  createFileSync: createFileSync
+  createFileSync: createFileSync,
+  // alias
+  ensureFile: createFile,
+  ensureFileSync: createFileSync
 }
diff --git a/lib/ensure/index.js b/lib/ensure/index.js
new file mode 100644
index 0000000..26e8705
--- /dev/null
+++ b/lib/ensure/index.js
@@ -0,0 +1,21 @@
+var file = require('./file')
+var link = require('./link')
+var symlink = require('./symlink')
+
+module.exports = {
+  // file
+  createFile: file.createFile,
+  createFileSync: file.createFileSync,
+  ensureFile: file.createFile,
+  ensureFileSync: file.createFileSync,
+  // link
+  createLink: link.createLink,
+  createLinkSync: link.createLinkSync,
+  ensureLink: link.createLink,
+  ensureLinkSync: link.createLinkSync,
+  // symlink
+  createSymlink: symlink.createSymlink,
+  createSymlinkSync: symlink.createSymlinkSync,
+  ensureSymlink: symlink.createSymlink,
+  ensureSymlinkSync: symlink.createSymlinkSync
+}
diff --git a/lib/ensure/link.js b/lib/ensure/link.js
new file mode 100644
index 0000000..4e4e283
--- /dev/null
+++ b/lib/ensure/link.js
@@ -0,0 +1,58 @@
+var path = require('path')
+var fs = require('graceful-fs')
+var mkdir = require('../mkdirs')
+
+function createLink (srcpath, dstpath, callback) {
+  function makeLink (srcpath, dstpath) {
+    fs.link(srcpath, dstpath, function (err) {
+      if (err) return callback(err)
+      callback(null)
+    })
+  }
+
+  fs.exists(dstpath, function (destinationExists) {
+    if (destinationExists) return callback(null)
+    fs.lstat(srcpath, function (err, stat) {
+      if (err) {
+        err.message = err.message.replace('lstat', 'ensureLink')
+        return callback(err)
+      }
+
+      var dir = path.dirname(dstpath)
+      fs.exists(dir, function (dirExists) {
+        if (dirExists) return makeLink(srcpath, dstpath)
+        mkdir.mkdirs(dir, function (err) {
+          if (err) return callback(err)
+          makeLink(srcpath, dstpath)
+        })
+      })
+    })
+  })
+}
+
+function createLinkSync (srcpath, dstpath, callback) {
+  var destinationExists = fs.existsSync(dstpath)
+  if (destinationExists) return undefined
+
+  try {
+    fs.lstatSync(srcpath)
+  } catch (err) {
+    err.message = err.message.replace('lstat', 'ensureLink')
+    throw err
+  }
+
+  var dir = path.dirname(dstpath)
+  var dirExists = fs.existsSync(dir)
+  if (dirExists) return fs.linkSync(srcpath, dstpath)
+  mkdir.mkdirsSync(dir)
+
+  return fs.linkSync(srcpath, dstpath)
+}
+
+module.exports = {
+  createLink: createLink,
+  createLinkSync: createLinkSync,
+  // alias
+  ensureLink: createLink,
+  ensureLinkSync: createLinkSync
+}
diff --git a/lib/ensure/symlink-paths.js b/lib/ensure/symlink-paths.js
new file mode 100644
index 0000000..eebc12e
--- /dev/null
+++ b/lib/ensure/symlink-paths.js
@@ -0,0 +1,96 @@
+var path = require('path')
+// path.isAbsolute shim for Node.js 0.10 support
+var fs = require('graceful-fs')
+
+/**
+ * Function that returns two types of paths, one relative to symlink, and one
+ * relative to the current working directory. Checks if path is absolute or
+ * relative. If the path is relative, this function checks if the path is
+ * relative to symlink or relative to current working directory. This is an
+ * initiative to find a smarter `srcpath` to supply when building symlinks.
+ * This allows you to determine which path to use out of one of three possible
+ * types of source paths. The first is an absolute path. This is detected by
+ * `path.isAbsolute()`. When an absolute path is provided, it is checked to
+ * see if it exists. If it does it's used, if not an error is returned
+ * (callback)/ thrown (sync). The other two options for `srcpath` are a
+ * relative url. By default Node's `fs.symlink` works by creating a symlink
+ * using `dstpath` and expects the `srcpath` to be relative to the newly
+ * created symlink. If you provide a `srcpath` that does not exist on the file
+ * system it results in a broken symlink. To minimize this, the function
+ * checks to see if the 'relative to symlink' source file exists, and if it
+ * does it will use it. If it does not, it checks if there's a file that
+ * exists that is relative to the current working directory, if does its used.
+ * This preserves the expectations of the original fs.symlink spec and adds
+ * the ability to pass in `relative to current working direcotry` paths.
+ */
+
+function symlinkPaths (srcpath, dstpath, callback) {
+  if (path.isAbsolute(srcpath)) {
+    return fs.lstat(srcpath, function (err, stat) {
+      if (err) {
+        err.message = err.message.replace('lstat', 'ensureSymlink')
+        return callback(err)
+      }
+      return callback(null, {
+        'toCwd': srcpath,
+        'toDst': srcpath
+      })
+    })
+  } else {
+    var dstdir = path.dirname(dstpath)
+    var relativeToDst = path.join(dstdir, srcpath)
+    return fs.exists(relativeToDst, function (exists) {
+      if (exists) {
+        return callback(null, {
+          'toCwd': relativeToDst,
+          'toDst': srcpath
+        })
+      } else {
+        return fs.lstat(srcpath, function (err, stat) {
+          if (err) {
+            err.message = err.message.replace('lstat', 'ensureSymlink')
+            return callback(err)
+          }
+          return callback(null, {
+            'toCwd': srcpath,
+            'toDst': path.relative(dstdir, srcpath)
+          })
+        })
+      }
+    })
+  }
+}
+
+function symlinkPathsSync (srcpath, dstpath) {
+  var exists
+  if (path.isAbsolute(srcpath)) {
+    exists = fs.existsSync(srcpath)
+    if (!exists) throw new Error('absolute srcpath does not exist')
+    return {
+      'toCwd': srcpath,
+      'toDst': srcpath
+    }
+  } else {
+    var dstdir = path.dirname(dstpath)
+    var relativeToDst = path.join(dstdir, srcpath)
+    exists = fs.existsSync(relativeToDst)
+    if (exists) {
+      return {
+        'toCwd': relativeToDst,
+        'toDst': srcpath
+      }
+    } else {
+      exists = fs.existsSync(srcpath)
+      if (!exists) throw new Error('relative srcpath does not exist')
+      return {
+        'toCwd': srcpath,
+        'toDst': path.relative(dstdir, srcpath)
+      }
+    }
+  }
+}
+
+module.exports = {
+  'symlinkPaths': symlinkPaths,
+  'symlinkPathsSync': symlinkPathsSync
+}
diff --git a/lib/ensure/symlink-type.js b/lib/ensure/symlink-type.js
new file mode 100644
index 0000000..81e3588
--- /dev/null
+++ b/lib/ensure/symlink-type.js
@@ -0,0 +1,27 @@
+var fs = require('graceful-fs')
+
+function symlinkType (srcpath, type, callback) {
+  callback = (typeof type === 'function') ? type : callback
+  type = (typeof type === 'function') ? false : type
+  if (type) return callback(null, type)
+  fs.lstat(srcpath, function (err, stats) {
+    if (err) return callback(null, 'file')
+    type = (stats && stats.isDirectory()) ? 'dir' : 'file'
+    callback(null, type)
+  })
+}
+
+function symlinkTypeSync (srcpath, type) {
+  if (type) return type
+  try {
+    var stats = fs.lstatSync(srcpath)
+  } catch (e) {
+    return 'file'
+  }
+  return (stats && stats.isDirectory()) ? 'dir' : 'file'
+}
+
+module.exports = {
+  symlinkType: symlinkType,
+  symlinkTypeSync: symlinkTypeSync
+}
diff --git a/lib/ensure/symlink.js b/lib/ensure/symlink.js
new file mode 100644
index 0000000..6244790
--- /dev/null
+++ b/lib/ensure/symlink.js
@@ -0,0 +1,62 @@
+var path = require('path')
+var fs = require('graceful-fs')
+var _mkdirs = require('../mkdirs')
+var mkdirs = _mkdirs.mkdirs
+var mkdirsSync = _mkdirs.mkdirsSync
+
+var _symlinkPaths = require('./symlink-paths')
+var symlinkPaths = _symlinkPaths.symlinkPaths
+var symlinkPathsSync = _symlinkPaths.symlinkPathsSync
+
+var _symlinkType = require('./symlink-type')
+var symlinkType = _symlinkType.symlinkType
+var symlinkTypeSync = _symlinkType.symlinkTypeSync
+
+function createSymlink (srcpath, dstpath, type, callback) {
+  callback = (typeof type === 'function') ? type : callback
+  type = (typeof type === 'function') ? false : type
+
+  fs.exists(dstpath, function (destinationExists) {
+    if (destinationExists) return callback(null)
+    symlinkPaths(srcpath, dstpath, function (err, relative) {
+      if (err) return callback(err)
+      srcpath = relative.toDst
+      symlinkType(relative.toCwd, type, function (err, type) {
+        if (err) return callback(err)
+        var dir = path.dirname(dstpath)
+        fs.exists(dir, function (dirExists) {
+          if (dirExists) return fs.symlink(srcpath, dstpath, type, callback)
+          mkdirs(dir, function (err) {
+            if (err) return callback(err)
+            fs.symlink(srcpath, dstpath, type, callback)
+          })
+        })
+      })
+    })
+  })
+}
+
+function createSymlinkSync (srcpath, dstpath, type, callback) {
+  callback = (typeof type === 'function') ? type : callback
+  type = (typeof type === 'function') ? false : type
+
+  var destinationExists = fs.existsSync(dstpath)
+  if (destinationExists) return undefined
+
+  var relative = symlinkPathsSync(srcpath, dstpath)
+  srcpath = relative.toDst
+  type = symlinkTypeSync(relative.toCwd, type)
+  var dir = path.dirname(dstpath)
+  var exists = fs.existsSync(dir)
+  if (exists) return fs.symlinkSync(srcpath, dstpath, type)
+  mkdirsSync(dir)
+  return fs.symlinkSync(srcpath, dstpath, type)
+}
+
+module.exports = {
+  createSymlink: createSymlink,
+  createSymlinkSync: createSymlinkSync,
+  // alias
+  ensureSymlink: createSymlink,
+  ensureSymlinkSync: createSymlinkSync
+}
diff --git a/lib/index.js b/lib/index.js
index 25c73ab..7b874d5 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,90 +1,38 @@
-var jsonFile = require('jsonfile')
-var json = require('./json')
+var assign = require('./util/assign')
 
 var fse = {}
-var fs = require("graceful-fs")
+var gfs = require('graceful-fs')
 
-//attach fs methods to fse
-Object.keys(fs).forEach(function(key) {
-  var func = fs[key]
-  if (typeof func == 'function')
-    fse[key] = func
+// attach fs methods to fse
+Object.keys(gfs).forEach(function (key) {
+  fse[key] = gfs[key]
 })
-fs = fse
 
-var copy = require('./copy')
-fs.copy = copy.copy
-fs.copySync = copy.copySync
-
-var remove = require('./remove')
-fs.remove = remove.remove
-fs.removeSync = remove.removeSync
-fs['delete'] = fs.remove
-fs.deleteSync = fs.removeSync
-
-var mkdir = require('./mkdir')
-fs.mkdirs = mkdir.mkdirs
-fs.mkdirsSync = mkdir.mkdirsSync
-fs.mkdirp = fs.mkdirs
-fs.mkdirpSync = fs.mkdirsSync
-
-var create = require('./create')
-fs.createFile = create.createFile
-fs.createFileSync = create.createFileSync
-
-fs.ensureFile = create.createFile
-fs.ensureFileSync = create.createFileSync
-fs.ensureDir = mkdir.mkdirs
-fs.ensureDirSync = mkdir.mkdirsSync
-
-
-var move = require('./move')
-fs.move = function(src, dest, opts, callback) {
-  if (typeof opts == 'function') {
-    callback = opts
-    opts = {}
-  }
-
-  if (opts.mkdirp == null) opts.mkdirp = true
-  if (opts.clobber == null) opts.clobber = false
-
-  move(src, dest, opts, callback)
-}
-
-
-var output = require('./output')
-fs.outputFile = output.outputFile
-fs.outputFileSync = output.outputFileSync
-
-
-fs.readJsonFile = jsonFile.readFile
-fs.readJSONFile = jsonFile.readFile
-fs.readJsonFileSync = jsonFile.readFileSync
-fs.readJSONFileSync = jsonFile.readFileSync
-
-fs.readJson = jsonFile.readFile
-fs.readJSON = jsonFile.readFile
-fs.readJsonSync = jsonFile.readFileSync
-fs.readJSONSync = jsonFile.readFileSync
-
-fs.outputJsonSync = json.outputJsonSync
-fs.outputJSONSync = json.outputJsonSync
-fs.outputJson = json.outputJson
-fs.outputJSON = json.outputJson
-
-fs.writeJsonFile = jsonFile.writeFile
-fs.writeJSONFile = jsonFile.writeFile
-fs.writeJsonFileSync = jsonFile.writeFileSync
-fs.writeJSONFileSync = jsonFile.writeFileSync
-
-fs.writeJson = jsonFile.writeFile
-fs.writeJSON = jsonFile.writeFile
-fs.writeJsonSync = jsonFile.writeFileSync
-fs.writeJSONSync = jsonFile.writeFileSync
+var fs = fse
 
+assign(fs, require('./copy'))
+assign(fs, require('./copy-sync'))
+assign(fs, require('./mkdirs'))
+assign(fs, require('./remove'))
+assign(fs, require('./json'))
+assign(fs, require('./move'))
+assign(fs, require('./empty'))
+assign(fs, require('./ensure'))
+assign(fs, require('./output'))
+assign(fs, require('./walk'))
+assign(fs, require('./walk-sync'))
 
 module.exports = fs
 
-jsonFile.spaces = 2 //set to 2
-module.exports.jsonfile = jsonFile //so users of fs-extra can modify jsonFile.spaces
+// maintain backwards compatibility for awhile
+var jsonfile = {}
+Object.defineProperty(jsonfile, 'spaces', {
+  get: function () {
+    return fs.spaces // found in ./json
+  },
+  set: function (val) {
+    fs.spaces = val
+  }
+})
 
+module.exports.jsonfile = jsonfile // so users of fs-extra can modify jsonFile.spaces
diff --git a/lib/json.js b/lib/json.js
deleted file mode 100644
index 8e12498..0000000
--- a/lib/json.js
+++ /dev/null
@@ -1,31 +0,0 @@
-var fs = require('graceful-fs')
-var path = require('path')
-var jsonFile = require('jsonfile')
-var mkdir = require('./mkdir')
-
-function outputJsonSync(file, data) {
-  var dir = path.dirname(file)
-
-  if (!fs.existsSync(dir))
-    mkdir.mkdirsSync(dir)
-
-  jsonFile.writeFileSync(file, data)
-}
-
-function outputJson(file, data, callback) {
-  var dir = path.dirname(file)
-
-  fs.exists(dir, function(itDoes) {
-    if (itDoes) return jsonFile.writeFile(file, data, callback)
-
-    mkdir.mkdirs(dir, function(err) {
-      if (err) return callback(err)
-      jsonFile.writeFile(file, data, callback)
-    })
-  })
-}
-
-module.exports = {
-  outputJsonSync: outputJsonSync,
-  outputJson: outputJson
-}
diff --git a/lib/json/__tests__/jsonfile-integration.test.js b/lib/json/__tests__/jsonfile-integration.test.js
new file mode 100644
index 0000000..925b2dc
--- /dev/null
+++ b/lib/json/__tests__/jsonfile-integration.test.js
@@ -0,0 +1,39 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+
+/* global afterEach, beforeEach, describe, it */
+
+describe('jsonfile-integration', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'json')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  describe('+ writeJsonSync / spaces', function () {
+    it('should read a file and parse the json', function () {
+      var obj1 = {
+        firstName: 'JP',
+        lastName: 'Richardson'
+      }
+
+      var oldSpaces = fse.jsonfile.spaces
+      fse.jsonfile.spaces = 4
+
+      var file = path.join(TEST_DIR, 'file.json')
+      fse.writeJsonSync(file, obj1)
+      var data = fs.readFileSync(file, 'utf8')
+      assert.strictEqual(data, JSON.stringify(obj1, null, 4) + '\n')
+
+      fse.jsonfile.spaces = oldSpaces
+    })
+  })
+})
diff --git a/lib/json/__tests__/output-json-sync.test.js b/lib/json/__tests__/output-json-sync.test.js
new file mode 100644
index 0000000..9d4ddd0
--- /dev/null
+++ b/lib/json/__tests__/output-json-sync.test.js
@@ -0,0 +1,48 @@
+var assert = require('assert')
+var fs = require('fs')
+var os = require('os')
+var path = require('path')
+var fse = require(process.cwd())
+
+/* global beforeEach, describe, it */
+
+describe('json', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  describe('+ outputJsonSync(file, data)', function () {
+    var outputJsonSync = require('../output-json-sync')
+
+    it('should write the file regardless of whether the directory exists or not', function () {
+      var file = path.join(TEST_DIR, 'this-dir', 'does-not', 'exist', 'file.json')
+      assert(!fs.existsSync(file))
+
+      var data = {name: 'JP'}
+      outputJsonSync(file, data)
+
+      assert(fs.existsSync(file))
+      var newData = JSON.parse(fs.readFileSync(file, 'utf8'))
+
+      assert.equal(data.name, newData.name)
+    })
+
+    describe('> when an option is passed, like JSON replacer', function () {
+      it('should pass the option along to jsonfile module', function () {
+        var file = path.join(TEST_DIR, 'this-dir', 'does-not', 'exist', 'really', 'file.json')
+        assert(!fs.existsSync(file))
+
+        var replacer = function (k, v) { if (v === 'JP') return 'Jon Paul'; else return v }
+        var data = {name: 'JP'}
+
+        outputJsonSync(file, data, {replacer: replacer})
+        var newData = JSON.parse(fs.readFileSync(file, 'utf8'))
+
+        assert.equal(newData.name, 'Jon Paul')
+      })
+    })
+  })
+})
diff --git a/lib/json/__tests__/output-json.test.js b/lib/json/__tests__/output-json.test.js
new file mode 100644
index 0000000..8e6cd4c
--- /dev/null
+++ b/lib/json/__tests__/output-json.test.js
@@ -0,0 +1,53 @@
+var assert = require('assert')
+var fs = require('fs')
+var os = require('os')
+var path = require('path')
+var fse = require(process.cwd())
+
+/* global beforeEach, describe, it */
+
+describe('json', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  describe('+ outputJson(file, data)', function () {
+    var outputJson = require('../output-json')
+
+    it('should write the file regardless of whether the directory exists or not', function (done) {
+      var file = path.join(TEST_DIR, 'this-dir', 'prob-does-not', 'exist', 'file.json')
+      assert(!fs.existsSync(file))
+
+      var data = {name: 'JP'}
+      outputJson(file, data, function (err) {
+        if (err) return done(err)
+
+        assert(fs.existsSync(file))
+        var newData = JSON.parse(fs.readFileSync(file, 'utf8'))
+
+        assert.equal(data.name, newData.name)
+        done()
+      })
+    })
+
+    describe('> when an option is passed, like JSON replacer', function () {
+      it('should pass the option along to jsonfile module', function (done) {
+        var file = path.join(TEST_DIR, 'this-dir', 'does-not', 'exist', 'really', 'file.json')
+        assert(!fs.existsSync(file))
+
+        var replacer = function (k, v) { if (v === 'JP') return 'Jon Paul'; else return v }
+        var data = {name: 'JP'}
+
+        outputJson(file, data, {replacer: replacer}, function (err) {
+          assert.ifError(err)
+          var newData = JSON.parse(fs.readFileSync(file, 'utf8'))
+          assert.equal(newData.name, 'Jon Paul')
+          done()
+        })
+      })
+    })
+  })
+})
diff --git a/test/read.test.js b/lib/json/__tests__/read.test.js
similarity index 57%
rename from test/read.test.js
rename to lib/json/__tests__/read.test.js
index 8a5a32f..34bee4c 100644
--- a/test/read.test.js
+++ b/lib/json/__tests__/read.test.js
@@ -1,24 +1,25 @@
 var assert = require('assert')
 var fs = require('fs')
 var path = require('path')
-var fse = require('../')
-var testutil = require('testutil')
+var os = require('os')
+var fse = require(process.cwd())
 
 /* global afterEach, beforeEach, describe, it */
 
-var TEST_DIR = ''
+describe('read', function () {
+  var TEST_DIR
 
-describe('read', function() {
-  beforeEach(function() {
-    TEST_DIR = testutil.createTestDir('fs-extra')
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'read-json')
+    fse.emptyDir(TEST_DIR, done)
   })
 
-  afterEach(function(done) {
+  afterEach(function (done) {
     fse.remove(TEST_DIR, done)
   })
 
-  describe('+ readJSON', function() {
-    it('should read a file and parse the json', function(done) {
+  describe('+ readJSON', function () {
+    it('should read a file and parse the json', function (done) {
       var obj1 = {
         firstName: 'JP',
         lastName: 'Richardson'
@@ -26,7 +27,7 @@ describe('read', function() {
 
       var file = path.join(TEST_DIR, 'file.json')
       fs.writeFileSync(file, JSON.stringify(obj1))
-      fse.readJSON(file, function(err, obj2) {
+      fse.readJSON(file, function (err, obj2) {
         assert.ifError(err)
         assert.strictEqual(obj1.firstName, obj2.firstName)
         assert.strictEqual(obj1.lastName, obj2.lastName)
@@ -35,10 +36,10 @@ describe('read', function() {
       })
     })
 
-    it('should error if it cant parse the json', function(done) {
+    it('should error if it cant parse the json', function (done) {
       var file = path.join(TEST_DIR, 'file2.json')
       fs.writeFileSync(file, '%asdfasdff444')
-      fse.readJSON(file, function(err, obj) {
+      fse.readJSON(file, function (err, obj) {
         assert(err)
         assert(!obj)
         done()
diff --git a/lib/json/__tests__/spaces.test.js b/lib/json/__tests__/spaces.test.js
new file mode 100644
index 0000000..f878688
--- /dev/null
+++ b/lib/json/__tests__/spaces.test.js
@@ -0,0 +1,34 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require('../../')
+
+/* global afterEach, beforeEach, describe, it */
+// trinity: mocha
+
+describe('json spaces', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'json-spaces')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('should write out the file with appropriate spacing (2)', function () {
+    fse.spaces = 2 // for legacy compatibility
+    assert.strictEqual(fse.spaces, 2)
+
+    var tempFile = path.join(TEST_DIR, 'temp.json')
+
+    var data = { first: 'JP', last: 'Richardson' }
+    fse.outputJsonSync(tempFile, data)
+
+    var dataRead = fs.readFileSync(tempFile, 'utf8')
+    assert.strictEqual(dataRead, JSON.stringify(data, null, 2) + '\n')
+  })
+})
diff --git a/lib/json/index.js b/lib/json/index.js
new file mode 100644
index 0000000..b13cf54
--- /dev/null
+++ b/lib/json/index.js
@@ -0,0 +1,9 @@
+var jsonFile = require('./jsonfile')
+
+jsonFile.outputJsonSync = require('./output-json-sync')
+jsonFile.outputJson = require('./output-json')
+// aliases
+jsonFile.outputJSONSync = require('./output-json-sync')
+jsonFile.outputJSON = require('./output-json')
+
+module.exports = jsonFile
diff --git a/lib/json/jsonfile.js b/lib/json/jsonfile.js
new file mode 100644
index 0000000..51d8390
--- /dev/null
+++ b/lib/json/jsonfile.js
@@ -0,0 +1,14 @@
+var jsonFile = require('jsonfile')
+
+module.exports = {
+  // jsonfile exports
+  readJson: jsonFile.readFile,
+  readJSON: jsonFile.readFile,
+  readJsonSync: jsonFile.readFileSync,
+  readJSONSync: jsonFile.readFileSync,
+  writeJson: jsonFile.writeFile,
+  writeJSON: jsonFile.writeFile,
+  writeJsonSync: jsonFile.writeFileSync,
+  writeJSONSync: jsonFile.writeFileSync,
+  spaces: 2 // default in fs-extra
+}
diff --git a/lib/json/output-json-sync.js b/lib/json/output-json-sync.js
new file mode 100644
index 0000000..7684843
--- /dev/null
+++ b/lib/json/output-json-sync.js
@@ -0,0 +1,16 @@
+var fs = require('graceful-fs')
+var path = require('path')
+var jsonFile = require('./jsonfile')
+var mkdir = require('../mkdirs')
+
+function outputJsonSync (file, data, options) {
+  var dir = path.dirname(file)
+
+  if (!fs.existsSync(dir)) {
+    mkdir.mkdirsSync(dir)
+  }
+
+  jsonFile.writeJsonSync(file, data, options)
+}
+
+module.exports = outputJsonSync
diff --git a/lib/json/output-json.js b/lib/json/output-json.js
new file mode 100644
index 0000000..7824597
--- /dev/null
+++ b/lib/json/output-json.js
@@ -0,0 +1,24 @@
+var fs = require('graceful-fs')
+var path = require('path')
+var jsonFile = require('./jsonfile')
+var mkdir = require('../mkdirs')
+
+function outputJson (file, data, options, callback) {
+  if (typeof options === 'function') {
+    callback = options
+    options = {}
+  }
+
+  var dir = path.dirname(file)
+
+  fs.exists(dir, function (itDoes) {
+    if (itDoes) return jsonFile.writeJson(file, data, options, callback)
+
+    mkdir.mkdirs(dir, function (err) {
+      if (err) return callback(err)
+      jsonFile.writeJson(file, data, options, callback)
+    })
+  })
+}
+
+module.exports = outputJson
diff --git a/lib/mkdir.js b/lib/mkdir.js
deleted file mode 100644
index 6ac2e86..0000000
--- a/lib/mkdir.js
+++ /dev/null
@@ -1,103 +0,0 @@
-var fs = require('graceful-fs')
-var path = require('path')
-
-var octal_0777 = parseInt('0777', 8)
-
-function mkdirs(p, opts, f, made) {
-  if (typeof opts === 'function') {
-    f = opts
-    opts = {}
-  }
-  else if (!opts || typeof opts !== 'object') {
-    opts = { mode: opts }
-  }
-
-  var mode = opts.mode
-  var xfs = opts.fs || fs
-
-  if (mode === undefined) {
-    mode = octal_0777 & (~process.umask())
-  }
-  if (!made) made = null
-
-  var cb = f || function () {}
-  p = path.resolve(p)
-
-  xfs.mkdir(p, mode, function (er) {
-    if (!er) {
-      made = made || p
-      return cb(null, made)
-    }
-    switch (er.code) {
-      case 'ENOENT':
-        if (path.dirname(p) == p) return cb(er)
-        mkdirs(path.dirname(p), opts, function (er, made) {
-          if (er) cb(er, made)
-          else mkdirs(p, opts, cb, made)
-        })
-        break
-
-      // In the case of any other error, just see if there's a dir
-      // there already.  If so, then hooray!  If not, then something
-      // is borked.
-      default:
-        xfs.stat(p, function (er2, stat) {
-          // if the stat fails, then that's super weird.
-          // let the original error be the failure reason.
-          if (er2 || !stat.isDirectory()) cb(er, made)
-          else cb(null, made)
-        })
-        break
-    }
-  })
-}
-
-function mkdirsSync (p, opts, made) {
-  if (!opts || typeof opts !== 'object') {
-    opts = { mode: opts }
-  }
-
-  var mode = opts.mode
-  var xfs = opts.fs || fs
-
-  if (mode === undefined) {
-    mode = octal_0777 & (~process.umask())
-  }
-  if (!made) made = null
-
-  p = path.resolve(p)
-
-  try {
-    xfs.mkdirSync(p, mode)
-    made = made || p
-  }
-  catch (err0) {
-    switch (err0.code) {
-      case 'ENOENT' :
-        made = mkdirsSync(path.dirname(p), opts, made)
-        mkdirsSync(p, opts, made)
-        break
-
-      // In the case of any other error, just see if there's a dir
-      // there already.  If so, then hooray!  If not, then something
-      // is borked.
-      default:
-        var stat
-        try {
-          stat = xfs.statSync(p)
-        }
-        catch (err1) {
-          throw err0
-        }
-        if (!stat.isDirectory()) throw err0
-        break
-    }
-  }
-
-  return made
-}
-
-module.exports = {
-  mkdirs: mkdirs,
-  mkdirsSync: mkdirsSync
-}
diff --git a/lib/mkdirs/__tests__/chmod.test.js b/lib/mkdirs/__tests__/chmod.test.js
new file mode 100644
index 0000000..cb1b18a
--- /dev/null
+++ b/lib/mkdirs/__tests__/chmod.test.js
@@ -0,0 +1,67 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require('../../')
+
+/* global afterEach, beforeEach, describe, it */
+
+var o755 = parseInt('755', 8)
+var o744 = parseInt('744', 8)
+var o777 = parseInt('777', 8)
+var o666 = parseInt('666', 8)
+
+describe('mkdirp / chmod', function () {
+  var TEST_DIR
+  var TEST_SUBDIR
+
+  beforeEach(function (done) {
+    var ps = []
+    for (var i = 0; i < 15; i++) {
+      var dir = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+      ps.push(dir)
+    }
+
+    TEST_SUBDIR = ps.join(path.sep)
+
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'mkdirp-chmod')
+    TEST_SUBDIR = path.join(TEST_DIR, TEST_SUBDIR)
+
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('chmod-pre', function (done) {
+    var mode = o744
+    fse.mkdirp(TEST_SUBDIR, mode, function (er) {
+      assert.ifError(er, 'should not error')
+      fs.stat(TEST_SUBDIR, function (er, stat) {
+        assert.ifError(er, 'should exist')
+        assert.ok(stat && stat.isDirectory(), 'should be directory')
+
+        if (os.platform().indexOf('win') === 0) {
+          assert.equal(stat && stat.mode & o777, o666, 'windows shit')
+        } else {
+          assert.equal(stat && stat.mode & o777, mode, 'should be 0744')
+        }
+
+        done()
+      })
+    })
+  })
+
+  it('chmod', function (done) {
+    var mode = o755
+    fse.mkdirp(TEST_SUBDIR, mode, function (er) {
+      assert.ifError(er, 'should not error')
+      fs.stat(TEST_SUBDIR, function (er, stat) {
+        assert.ifError(er, 'should exist')
+        assert.ok(stat && stat.isDirectory(), 'should be directory')
+        done()
+      })
+    })
+  })
+})
diff --git a/lib/mkdirs/__tests__/clobber.test.js b/lib/mkdirs/__tests__/clobber.test.js
new file mode 100644
index 0000000..f69533b
--- /dev/null
+++ b/lib/mkdirs/__tests__/clobber.test.js
@@ -0,0 +1,53 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+
+/* global before, describe, it */
+
+var o755 = parseInt('755', 8)
+
+describe('mkdirp / clobber', function () {
+  var TEST_DIR
+  var file
+
+  before(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'mkdirp-clobber')
+    fse.emptyDir(TEST_DIR, function (err) {
+      assert.ifError(err)
+
+      var ps = [TEST_DIR]
+
+      for (var i = 0; i < 15; i++) {
+        var dir = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+        ps.push(dir)
+      }
+
+      file = ps.join(path.sep)
+
+      // a file in the way
+      var itw = ps.slice(0, 2).join(path.sep)
+
+      fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.')
+
+      fs.stat(itw, function (er, stat) {
+        assert.ifError(er)
+        assert.ok(stat && stat.isFile(), 'should be file')
+        done()
+      })
+    })
+  })
+
+  it('should clobber', function (done) {
+    fse.mkdirp(file, o755, function (err) {
+      assert.ok(err)
+      if (os.platform().indexOf('win') === 0) {
+        assert.equal(err.code, 'EEXIST')
+      } else {
+        assert.equal(err.code, 'ENOTDIR')
+      }
+      done()
+    })
+  })
+})
diff --git a/lib/mkdirs/__tests__/issue-209.test.js b/lib/mkdirs/__tests__/issue-209.test.js
new file mode 100644
index 0000000..2640eb9
--- /dev/null
+++ b/lib/mkdirs/__tests__/issue-209.test.js
@@ -0,0 +1,38 @@
+var assert = require('assert')
+var fse = require(process.cwd())
+
+/* global describe, it */
+
+describe('mkdirp: issue-209, win32, when bad path, should return a cleaner error', function () {
+  // only seems to be an issue on Windows.
+  if (process.platform !== 'win32') return
+
+  it('should return a callback', function (done) {
+    var file = './bad?dir'
+    fse.mkdirp(file, function (err) {
+      assert(err, 'error is present')
+      assert.strictEqual(err.code, 'EINVAL')
+
+      var file2 = 'c:\\tmp\foo:moo'
+      fse.mkdirp(file2, function (err) {
+        assert(err, 'error is present')
+        assert.strictEqual(err.code, 'EINVAL')
+        done()
+      })
+    })
+  })
+
+  describe('> sync', function () {
+    it('should throw an error', function () {
+      var didErr
+      try {
+        var file = 'c:\\tmp\foo:moo'
+        fse.mkdirpSync(file)
+      } catch (err) {
+        // console.error(err)
+        didErr = true
+      }
+      assert(didErr)
+    })
+  })
+})
diff --git a/lib/mkdirs/__tests__/issue-93.test.js b/lib/mkdirs/__tests__/issue-93.test.js
new file mode 100644
index 0000000..f7fc1fb
--- /dev/null
+++ b/lib/mkdirs/__tests__/issue-93.test.js
@@ -0,0 +1,36 @@
+var assert = require('assert')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+
+/* global before, describe, it */
+
+describe('mkdirp: issue-93, win32, when drive does not exist, it should return a cleaner error', function () {
+  var TEST_DIR
+
+  // only seems to be an issue on Windows.
+  if (process.platform !== 'win32') return
+
+  before(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'tests', 'fs-extra', 'mkdirp-issue-93')
+    fse.emptyDir(TEST_DIR, function (err) {
+      assert.ifError(err)
+      done()
+    })
+  })
+
+  it('should return a cleaner error than inifinite loop, stack crash', function (done) {
+    var file = 'R:\\afasd\\afaff\\fdfd' // hopefully drive 'r' does not exist on appveyor
+    fse.mkdirp(file, function (err) {
+      assert.strictEqual(err.code, 'ENOENT')
+
+      try {
+        fse.mkdirsSync(file)
+      } catch (err) {
+        assert.strictEqual(err.code, 'ENOENT')
+      }
+
+      done()
+    })
+  })
+})
diff --git a/test/mkdir.test.js b/lib/mkdirs/__tests__/mkdir.test.js
similarity index 62%
rename from test/mkdir.test.js
rename to lib/mkdirs/__tests__/mkdir.test.js
index 9c10c7e..adb3eeb 100644
--- a/test/mkdir.test.js
+++ b/lib/mkdirs/__tests__/mkdir.test.js
@@ -1,42 +1,43 @@
 var assert = require('assert')
 var fs = require('fs')
 var path = require('path')
-var testutil = require('testutil')
-var fse = require('../')
+var os = require('os')
+var fse = require(process.cwd())
 
 /* global afterEach, beforeEach, describe, it */
 
-var TEST_DIR = ''
+describe('fs-extra', function () {
+  var TEST_DIR
 
-describe('fs-extra', function() {
-  beforeEach(function() {
-    TEST_DIR = testutil.createTestDir('fs-extra')
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'mkdir')
+    fse.emptyDir(TEST_DIR, done)
   })
 
-  afterEach(function(done) {
+  afterEach(function (done) {
     fse.remove(TEST_DIR, done)
   })
 
-  describe('+ mkdirs()', function() {
-    it('should make the directory', function(done) {
+  describe('+ mkdirs()', function () {
+    it('should make the directory', function (done) {
       var dir = path.join(TEST_DIR, 'tmp-' + Date.now() + Math.random())
 
       assert(!fs.existsSync(dir))
 
-      fse.mkdirs(dir, function(err) {
+      fse.mkdirs(dir, function (err) {
         assert.ifError(err)
         assert(fs.existsSync(dir))
         done()
       })
     })
 
-    it('should make the entire directory path', function(done) {
+    it('should make the entire directory path', function (done) {
       var dir = path.join(TEST_DIR, 'tmp-' + Date.now() + Math.random())
       var newDir = path.join(TEST_DIR, 'dfdf', 'ffff', 'aaa')
 
       assert(!fs.existsSync(dir))
 
-      fse.mkdirs(newDir, function(err) {
+      fse.mkdirs(newDir, function (err) {
         assert.ifError(err)
         assert(fs.existsSync(newDir))
         done()
@@ -44,8 +45,8 @@ describe('fs-extra', function() {
     })
   })
 
-  describe('+ mkdirsSync()', function() {
-    it('should make the directory', function(done) {
+  describe('+ mkdirsSync()', function () {
+    it('should make the directory', function (done) {
       var dir = path.join(TEST_DIR, 'tmp-' + Date.now() + Math.random())
 
       assert(!fs.existsSync(dir))
@@ -55,7 +56,7 @@ describe('fs-extra', function() {
       done()
     })
 
-    it('should make the entire directory path', function(done) {
+    it('should make the entire directory path', function (done) {
       var dir = path.join(TEST_DIR, 'tmp-' + Date.now() + Math.random())
       var newDir = path.join(dir, 'dfdf', 'ffff', 'aaa')
 
diff --git a/lib/mkdirs/__tests__/mkdirp.test.js b/lib/mkdirs/__tests__/mkdirp.test.js
new file mode 100644
index 0000000..10b1f71
--- /dev/null
+++ b/lib/mkdirs/__tests__/mkdirp.test.js
@@ -0,0 +1,51 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+
+/* global afterEach, beforeEach, describe, it */
+
+var o755 = parseInt('755', 8)
+var o777 = parseInt('777', 8)
+var o666 = parseInt('666', 8)
+
+describe('mkdirp / mkdirp', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'mkdirp')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('should make the dir', function (done) {
+    var x = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+    var y = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+    var z = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+
+    var file = path.join(TEST_DIR, x, y, z)
+
+    fse.mkdirp(file, o755, function (err) {
+      assert.ifError(err)
+      fs.exists(file, function (ex) {
+        assert.ok(ex, 'file created')
+        fs.stat(file, function (err, stat) {
+          assert.ifError(err)
+
+          if (os.platform().indexOf('win') === 0) {
+            assert.equal(stat.mode & o777, o666)
+          } else {
+            assert.equal(stat.mode & o777, o755)
+          }
+
+          assert.ok(stat.isDirectory(), 'target not a directory')
+          done()
+        })
+      })
+    })
+  })
+})
diff --git a/lib/mkdirs/__tests__/opts-undef.test.js b/lib/mkdirs/__tests__/opts-undef.test.js
new file mode 100644
index 0000000..096f77e
--- /dev/null
+++ b/lib/mkdirs/__tests__/opts-undef.test.js
@@ -0,0 +1,29 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+var mkdirs = require('../mkdirs')
+
+/* global beforeEach, describe, it */
+
+describe('mkdirs / opts-undef', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'mkdirs')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  // https://github.com/substack/node-mkdirp/issues/45
+  it('should not hang', function (done) {
+    var newDir = path.join(TEST_DIR, 'doest', 'not', 'exist')
+    assert(!fs.existsSync(newDir))
+
+    mkdirs(newDir, undefined, function (err) {
+      assert.ifError(err)
+      assert(fs.existsSync(newDir))
+      done()
+    })
+  })
+})
diff --git a/lib/mkdirs/__tests__/perm.test.js b/lib/mkdirs/__tests__/perm.test.js
new file mode 100644
index 0000000..d265086
--- /dev/null
+++ b/lib/mkdirs/__tests__/perm.test.js
@@ -0,0 +1,54 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require('../../')
+
+/* global afterEach, beforeEach, describe, it */
+
+var o755 = parseInt('755', 8)
+var o777 = parseInt('777', 8)
+var o666 = parseInt('666', 8)
+
+describe('mkdirp / perm', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'mkdirp-perm')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('async perm', function (done) {
+    var file = path.join(TEST_DIR, (Math.random() * (1 << 30)).toString(16))
+
+    fse.mkdirp(file, o755, function (err) {
+      assert.ifError(err)
+      fs.exists(file, function (ex) {
+        assert.ok(ex, 'file created')
+        fs.stat(file, function (err, stat) {
+          assert.ifError(err)
+
+          if (os.platform().indexOf('win') === 0) {
+            assert.equal(stat.mode & o777, o666)
+          } else {
+            assert.equal(stat.mode & o777, o755)
+          }
+
+          assert.ok(stat.isDirectory(), 'target not a directory')
+          done()
+        })
+      })
+    })
+  })
+
+  it('async root perm', function (done) {
+    fse.mkdirp(path.join(os.tmpdir(), 'tmp'), o755, function (err) {
+      assert.ifError(err)
+      done()
+    })
+  })
+})
diff --git a/lib/mkdirs/__tests__/perm_sync.test.js b/lib/mkdirs/__tests__/perm_sync.test.js
new file mode 100644
index 0000000..f2fa7ce
--- /dev/null
+++ b/lib/mkdirs/__tests__/perm_sync.test.js
@@ -0,0 +1,58 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+
+/* global afterEach, beforeEach, describe, it */
+
+var o755 = parseInt('755', 8)
+var o777 = parseInt('777', 8)
+var o666 = parseInt('666', 8)
+
+describe('mkdirp / perm_sync', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'mkdirp-perm-sync')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('sync perm', function (done) {
+    var file = path.join(TEST_DIR, (Math.random() * (1 << 30)).toString(16) + '.json')
+
+    fse.mkdirpSync(file, o755)
+    fs.exists(file, function (ex) {
+      assert.ok(ex, 'file created')
+      fs.stat(file, function (err, stat) {
+        assert.ifError(err)
+
+        if (os.platform().indexOf('win') === 0) {
+          assert.equal(stat.mode & o777, o666)
+        } else {
+          assert.equal(stat.mode & o777, o755)
+        }
+
+        assert.ok(stat.isDirectory(), 'target not a directory')
+        done()
+      })
+    })
+  })
+
+  it('sync root perm', function (done) {
+    var file = TEST_DIR
+    fse.mkdirpSync(file, o755)
+    fs.exists(file, function (ex) {
+      assert.ok(ex, 'file created')
+      fs.stat(file, function (err, stat) {
+        assert.ifError(err)
+        assert.ok(stat.isDirectory(), 'target not a directory')
+        done()
+      })
+    })
+  })
+})
diff --git a/lib/mkdirs/__tests__/race.test.js b/lib/mkdirs/__tests__/race.test.js
new file mode 100644
index 0000000..9022df6
--- /dev/null
+++ b/lib/mkdirs/__tests__/race.test.js
@@ -0,0 +1,68 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+
+/* global afterEach, beforeEach, describe, it */
+
+var o755 = parseInt('755', 8)
+var o777 = parseInt('777', 8)
+var o666 = parseInt('666', 8)
+
+describe('mkdirp / race', function () {
+  var TEST_DIR, file
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'mkdirp-race')
+    fse.emptyDir(TEST_DIR, function (err) {
+      assert.ifError(err)
+
+      var ps = [TEST_DIR]
+
+      for (var i = 0; i < 15; i++) {
+        var dir = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+        ps.push(dir)
+      }
+
+      file = path.join.apply(path, ps)
+      done()
+    })
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('race', function (done) {
+    var res = 2
+    mk(file, function () {
+      if (--res === 0) done()
+    })
+
+    mk(file, function () {
+      if (--res === 0) done()
+    })
+
+    function mk (file, callback) {
+      fse.mkdirp(file, o755, function (err) {
+        assert.ifError(err)
+        fs.exists(file, function (ex) {
+          assert.ok(ex, 'file created')
+          fs.stat(file, function (err, stat) {
+            assert.ifError(err)
+
+            if (os.platform().indexOf('win') === 0) {
+              assert.equal(stat.mode & o777, o666)
+            } else {
+              assert.equal(stat.mode & o777, o755)
+            }
+
+            assert.ok(stat.isDirectory(), 'target not a directory')
+            if (callback) callback()
+          })
+        })
+      })
+    }
+  })
+})
diff --git a/lib/mkdirs/__tests__/rel.test.js b/lib/mkdirs/__tests__/rel.test.js
new file mode 100644
index 0000000..815d9d6
--- /dev/null
+++ b/lib/mkdirs/__tests__/rel.test.js
@@ -0,0 +1,61 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+
+/* global afterEach, beforeEach, describe, it */
+
+var o755 = parseInt('755', 8)
+var o777 = parseInt('777', 8)
+var o666 = parseInt('666', 8)
+
+describe('mkdirp / relative', function () {
+  var TEST_DIR, file
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'mkdirp-relative')
+    fse.emptyDir(TEST_DIR, function (err) {
+      assert.ifError(err)
+
+      var x = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+      var y = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+      var z = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+
+      // relative path
+      file = path.join(x, y, z)
+
+      done()
+    })
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('should make the directory with relative path', function (done) {
+    var cwd = process.cwd()
+    process.chdir(TEST_DIR)
+
+    fse.mkdirp(file, o755, function (err) {
+      assert.ifError(err)
+      fs.exists(file, function (ex) {
+        assert.ok(ex, 'file created')
+        fs.stat(file, function (err, stat) {
+          assert.ifError(err)
+          // restore
+          process.chdir(cwd)
+
+          if (os.platform().indexOf('win') === 0) {
+            assert.equal(stat.mode & o777, o666)
+          } else {
+            assert.equal(stat.mode & o777, o755)
+          }
+
+          assert.ok(stat.isDirectory(), 'target not a directory')
+          done()
+        })
+      })
+    })
+  })
+})
diff --git a/lib/mkdirs/__tests__/return.test.js b/lib/mkdirs/__tests__/return.test.js
new file mode 100644
index 0000000..fecea89
--- /dev/null
+++ b/lib/mkdirs/__tests__/return.test.js
@@ -0,0 +1,41 @@
+var assert = require('assert')
+var os = require('os')
+var path = require('path')
+var fse = require('../../')
+
+/* global afterEach, beforeEach, describe, it */
+
+describe('mkdirp / return value', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'mkdirp-return')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('should', function (done) {
+    var x = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+    var y = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+    var z = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+
+    var dir = TEST_DIR + path.sep
+    var file = dir + [x, y, z].join(path.sep)
+
+    // should return the first dir created.
+    // By this point, it would be profoundly surprising if /tmp didn't
+    // already exist, since every other test makes things in there.
+    fse.mkdirp(file, function (err, made) {
+      assert.ifError(err)
+      assert.equal(made, dir + x)
+      fse.mkdirp(file, function (err, made) {
+        assert.ifError(err)
+        assert.equal(made, null)
+        done()
+      })
+    })
+  })
+})
diff --git a/lib/mkdirs/__tests__/return_sync.test.js b/lib/mkdirs/__tests__/return_sync.test.js
new file mode 100644
index 0000000..d16dd90
--- /dev/null
+++ b/lib/mkdirs/__tests__/return_sync.test.js
@@ -0,0 +1,39 @@
+var assert = require('assert')
+var os = require('os')
+var path = require('path')
+var fse = require('../../')
+
+/* global afterEach, beforeEach, describe, it */
+
+describe('mkdirp / return value', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'mkdirp-return')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('should', function () {
+    var x = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+    var y = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+    var z = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+
+    var dir = TEST_DIR + path.sep
+    var file = dir + [x, y, z].join(path.sep)
+
+    // should return the first dir created.
+    // By this point, it would be profoundly surprising if /tmp didn't
+    // already exist, since every other test makes things in there.
+    // Note that this will throw on failure, which will fail the test.
+    var made = fse.mkdirpSync(file)
+    assert.equal(made, dir + x)
+
+    // making the same file again should have no effect.
+    made = fse.mkdirpSync(file)
+    assert.equal(made, null)
+  })
+})
diff --git a/lib/mkdirs/__tests__/root.test.js b/lib/mkdirs/__tests__/root.test.js
new file mode 100644
index 0000000..5311103
--- /dev/null
+++ b/lib/mkdirs/__tests__/root.test.js
@@ -0,0 +1,27 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var fse = require('../../')
+
+/* global describe, it */
+
+var o755 = parseInt('755', 8)
+
+describe('mkdirp / root', function () {
+  // '/' on unix, 'c:/' on windows.
+  var dir = path.normalize(path.resolve(path.sep)).toLowerCase()
+
+  // if not 'c:\\' or 'd:\\', it's probably a network mounted drive, this fails then. TODO: investigate
+  if (process.platform === 'win32' && (dir.indexOf('c:\\') === -1) && (dir.indexOf('d:\\') === -1)) return
+
+  it('should', function (done) {
+    fse.mkdirp(dir, o755, function (err) {
+      if (err) throw err
+      fs.stat(dir, function (er, stat) {
+        if (er) throw er
+        assert.ok(stat.isDirectory(), 'target is a directory')
+        done()
+      })
+    })
+  })
+})
diff --git a/lib/mkdirs/__tests__/sync.test.js b/lib/mkdirs/__tests__/sync.test.js
new file mode 100644
index 0000000..4290be1
--- /dev/null
+++ b/lib/mkdirs/__tests__/sync.test.js
@@ -0,0 +1,58 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+
+/* global afterEach, beforeEach, describe, it */
+
+var o755 = parseInt('755', 8)
+var o777 = parseInt('777', 8)
+var o666 = parseInt('666', 8)
+
+describe('mkdirp / sync', function () {
+  var TEST_DIR, file
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'mkdirp-sync')
+    fse.emptyDir(TEST_DIR, function (err) {
+      assert.ifError(err)
+
+      var x = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+      var y = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+      var z = Math.floor(Math.random() * Math.pow(16, 4)).toString(16)
+
+      file = path.join(TEST_DIR, x, y, z)
+
+      done()
+    })
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('should', function (done) {
+    try {
+      fse.mkdirpSync(file, o755)
+    } catch (err) {
+      assert.fail(err)
+    }
+
+    fs.exists(file, function (ex) {
+      assert.ok(ex, 'file created')
+      fs.stat(file, function (err, stat) {
+        assert.ifError(err)
+        // http://stackoverflow.com/questions/592448/c-how-to-set-file-permissions-cross-platform
+        if (os.platform().indexOf('win') === 0) {
+          assert.equal(stat.mode & o777, o666)
+        } else {
+          assert.equal(stat.mode & o777, o755)
+        }
+
+        assert.ok(stat.isDirectory(), 'target not a directory')
+        done()
+      })
+    })
+  })
+})
diff --git a/lib/mkdirs/__tests__/umask.test.js b/lib/mkdirs/__tests__/umask.test.js
new file mode 100644
index 0000000..c5bfc31
--- /dev/null
+++ b/lib/mkdirs/__tests__/umask.test.js
@@ -0,0 +1,80 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require('../../')
+
+/* global afterEach, beforeEach, describe, it */
+
+var o777 = parseInt('777', 8)
+
+describe('mkdirp', function () {
+  var TEST_DIR
+  var _rndDir
+
+  // should investigate this test and file more
+  if (os.platform().indexOf('win') === 0) return
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'mkdirp')
+    fse.emptyDir(TEST_DIR, function () {
+      // for actual tests
+      var x = Math.floor(Math.random() * Math.pow(16, 6)).toString(16)
+      var y = Math.floor(Math.random() * Math.pow(16, 6)).toString(16)
+      var z = Math.floor(Math.random() * Math.pow(16, 6)).toString(16)
+
+      _rndDir = path.join(TEST_DIR, [x, y, z].join(path.sep))
+
+      // just to be safe, although unnecessary
+      assert(!fs.existsSync(_rndDir))
+      done()
+    })
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  describe('umask', function () {
+    describe('async', function () {
+      it('should have proper umask', function (done) {
+        process.umask(0)
+
+        fse.mkdirp(_rndDir, function (err) {
+          assert.ifError(err)
+          fs.exists(_rndDir, function (ex) {
+            assert.ok(ex, 'file created')
+            fs.stat(_rndDir, function (err, stat) {
+              assert.ifError(err)
+              assert.equal(stat.mode & o777, o777 & (~process.umask()))
+              assert.ok(stat.isDirectory(), 'target not a directory')
+              done()
+            })
+          })
+        })
+      })
+    })
+
+    describe('sync', function () {
+      it('should have proper umask', function (done) {
+        process.umask(0)
+
+        try {
+          fse.mkdirpSync(_rndDir)
+        } catch (err) {
+          return done(err)
+        }
+
+        fs.exists(_rndDir, function (ex) {
+          assert.ok(ex, 'file created')
+          fs.stat(_rndDir, function (err, stat) {
+            assert.ifError(err)
+            assert.equal(stat.mode & o777, (o777 & (~process.umask())))
+            assert.ok(stat.isDirectory(), 'target not a directory')
+            done()
+          })
+        })
+      })
+    })
+  })
+})
diff --git a/lib/mkdirs/index.js b/lib/mkdirs/index.js
new file mode 100644
index 0000000..2611217
--- /dev/null
+++ b/lib/mkdirs/index.js
@@ -0,0 +1,9 @@
+module.exports = {
+  mkdirs: require('./mkdirs'),
+  mkdirsSync: require('./mkdirs-sync'),
+  // alias
+  mkdirp: require('./mkdirs'),
+  mkdirpSync: require('./mkdirs-sync'),
+  ensureDir: require('./mkdirs'),
+  ensureDirSync: require('./mkdirs-sync')
+}
diff --git a/lib/mkdirs/mkdirs-sync.js b/lib/mkdirs/mkdirs-sync.js
new file mode 100644
index 0000000..3f30680
--- /dev/null
+++ b/lib/mkdirs/mkdirs-sync.js
@@ -0,0 +1,57 @@
+var fs = require('graceful-fs')
+var path = require('path')
+var invalidWin32Path = require('./win32').invalidWin32Path
+
+var o777 = parseInt('0777', 8)
+
+function mkdirsSync (p, opts, made) {
+  if (!opts || typeof opts !== 'object') {
+    opts = { mode: opts }
+  }
+
+  var mode = opts.mode
+  var xfs = opts.fs || fs
+
+  if (process.platform === 'win32' && invalidWin32Path(p)) {
+    var errInval = new Error(p + ' contains invalid WIN32 path characters.')
+    errInval.code = 'EINVAL'
+    throw errInval
+  }
+
+  if (mode === undefined) {
+    mode = o777 & (~process.umask())
+  }
+  if (!made) made = null
+
+  p = path.resolve(p)
+
+  try {
+    xfs.mkdirSync(p, mode)
+    made = made || p
+  } catch (err0) {
+    switch (err0.code) {
+      case 'ENOENT':
+        if (path.dirname(p) === p) throw err0
+        made = mkdirsSync(path.dirname(p), opts, made)
+        mkdirsSync(p, opts, made)
+        break
+
+      // In the case of any other error, just see if there's a dir
+      // there already.  If so, then hooray!  If not, then something
+      // is borked.
+      default:
+        var stat
+        try {
+          stat = xfs.statSync(p)
+        } catch (err1) {
+          throw err0
+        }
+        if (!stat.isDirectory()) throw err0
+        break
+    }
+  }
+
+  return made
+}
+
+module.exports = mkdirsSync
diff --git a/lib/mkdirs/mkdirs.js b/lib/mkdirs/mkdirs.js
new file mode 100644
index 0000000..939776c
--- /dev/null
+++ b/lib/mkdirs/mkdirs.js
@@ -0,0 +1,61 @@
+var fs = require('graceful-fs')
+var path = require('path')
+var invalidWin32Path = require('./win32').invalidWin32Path
+
+var o777 = parseInt('0777', 8)
+
+function mkdirs (p, opts, callback, made) {
+  if (typeof opts === 'function') {
+    callback = opts
+    opts = {}
+  } else if (!opts || typeof opts !== 'object') {
+    opts = { mode: opts }
+  }
+
+  if (process.platform === 'win32' && invalidWin32Path(p)) {
+    var errInval = new Error(p + ' contains invalid WIN32 path characters.')
+    errInval.code = 'EINVAL'
+    return callback(errInval)
+  }
+
+  var mode = opts.mode
+  var xfs = opts.fs || fs
+
+  if (mode === undefined) {
+    mode = o777 & (~process.umask())
+  }
+  if (!made) made = null
+
+  callback = callback || function () {}
+  p = path.resolve(p)
+
+  xfs.mkdir(p, mode, function (er) {
+    if (!er) {
+      made = made || p
+      return callback(null, made)
+    }
+    switch (er.code) {
+      case 'ENOENT':
+        if (path.dirname(p) === p) return callback(er)
+        mkdirs(path.dirname(p), opts, function (er, made) {
+          if (er) callback(er, made)
+          else mkdirs(p, opts, callback, made)
+        })
+        break
+
+      // In the case of any other error, just see if there's a dir
+      // there already.  If so, then hooray!  If not, then something
+      // is borked.
+      default:
+        xfs.stat(p, function (er2, stat) {
+          // if the stat fails, then that's super weird.
+          // let the original error be the failure reason.
+          if (er2 || !stat.isDirectory()) callback(er, made)
+          else callback(null, made)
+        })
+        break
+    }
+  })
+}
+
+module.exports = mkdirs
diff --git a/lib/mkdirs/win32.js b/lib/mkdirs/win32.js
new file mode 100644
index 0000000..569ac1a
--- /dev/null
+++ b/lib/mkdirs/win32.js
@@ -0,0 +1,24 @@
+'use strict'
+var path = require('path')
+
+// get drive on windows
+function getRootPath (p) {
+  p = path.normalize(path.resolve(p)).split(path.sep)
+  if (p.length > 0) return p[0]
+  else return null
+}
+
+// http://stackoverflow.com/a/62888/10333 contains more accurate
+// TODO: expand to include the rest
+var INVALID_PATH_CHARS = /[<>:"|?*]/
+
+function invalidWin32Path (p) {
+  var rp = getRootPath(p)
+  p = p.replace(rp, '')
+  return INVALID_PATH_CHARS.test(p)
+}
+
+module.exports = {
+  getRootPath: getRootPath,
+  invalidWin32Path: invalidWin32Path
+}
diff --git a/test/fixtures/move/a-file b/lib/move/__tests__/fixtures/a-file
similarity index 100%
rename from test/fixtures/move/a-file
rename to lib/move/__tests__/fixtures/a-file
diff --git a/test/fixtures/move/a-folder/another-file b/lib/move/__tests__/fixtures/a-folder/another-file
similarity index 100%
rename from test/fixtures/move/a-folder/another-file
rename to lib/move/__tests__/fixtures/a-folder/another-file
diff --git a/test/fixtures/move/a-folder/another-folder/file3 b/lib/move/__tests__/fixtures/a-folder/another-folder/file3
similarity index 100%
rename from test/fixtures/move/a-folder/another-folder/file3
rename to lib/move/__tests__/fixtures/a-folder/another-folder/file3
diff --git a/lib/move/__tests__/move-clobber.test.js b/lib/move/__tests__/move-clobber.test.js
new file mode 100644
index 0000000..e89b770
--- /dev/null
+++ b/lib/move/__tests__/move-clobber.test.js
@@ -0,0 +1,68 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+
+/* global afterEach, beforeEach, describe, it */
+
+describe('move / clobber', function () {
+  var TEST_DIR, FIXTURES_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'move')
+    fse.emptyDir(TEST_DIR, function (err) {
+      assert.ifError(err)
+
+      FIXTURES_DIR = path.join(TEST_DIR, 'fixtures')
+      fse.remove(FIXTURES_DIR, function (err) {
+        assert.ifError(err)
+        fse.copy(path.join(__dirname, './fixtures'), FIXTURES_DIR, done)
+      })
+    })
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  describe('> when clobber = true', function () {
+    describe('> when dest is a directory', function () {
+      it('should clobber the destination', function (done) {
+        // use fixtures dir as dest since it has stuff
+        var dest = FIXTURES_DIR
+        var paths = fs.readdirSync(dest)
+
+        // verify dest has stuff
+        assert(paths.indexOf('a-file') >= 0)
+        assert(paths.indexOf('a-folder') >= 0)
+
+        // create new source dir
+        var src = path.join(TEST_DIR, 'src')
+        fse.ensureDirSync(src)
+        fse.mkdirsSync(path.join(src, 'some-folder'))
+        fs.writeFileSync(path.join(src, 'some-file'), 'hi')
+
+        // verify source has stuff
+        paths = fs.readdirSync(src)
+        assert(paths.indexOf('some-file') >= 0)
+        assert(paths.indexOf('some-folder') >= 0)
+
+        fse.move(src, dest, {clobber: true}, function (err) {
+          if (err) return done(err)
+
+          // verify dest does not have old stuff
+          var paths = fs.readdirSync(dest)
+          assert.strictEqual(paths.indexOf('a-file'), -1)
+          assert.strictEqual(paths.indexOf('a-folder'), -1)
+
+          // verify dest has new stuff
+          assert(paths.indexOf('some-file') >= 0)
+          assert(paths.indexOf('some-folder') >= 0)
+
+          done()
+        })
+      })
+    })
+  })
+})
diff --git a/lib/move/__tests__/move.test.js b/lib/move/__tests__/move.test.js
new file mode 100644
index 0000000..67e137d
--- /dev/null
+++ b/lib/move/__tests__/move.test.js
@@ -0,0 +1,285 @@
+var assert = require('assert')
+var os = require('os')
+var path = require('path')
+var rimraf = require('rimraf')
+var fs = require('graceful-fs')
+var fse = require(process.cwd())
+
+/* global afterEach, beforeEach, describe, it */
+
+var FIXTURES_DIR = ''
+var SRC_FIXTURES_DIR = path.join(__dirname, './fixtures')
+
+function createAsyncErrFn (errCode) {
+  var fn = function () {
+    fn.callCount++
+    var callback = arguments[arguments.length - 1]
+    setTimeout(function () {
+      var err = new Error()
+      err.code = errCode
+      callback(err)
+    }, 10)
+  }
+  fn.callCount = 0
+  return fn
+}
+
+var originalRename = fs.rename
+var originalLink = fs.link
+
+function setUpMockFs (errCode) {
+  fs.rename = createAsyncErrFn(errCode)
+  fs.link = createAsyncErrFn(errCode)
+}
+
+function tearDownMockFs () {
+  fs.rename = originalRename
+  fs.link = originalLink
+}
+
+describe('move', function () {
+  var TEST_DIR
+
+  beforeEach(function () {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'move')
+
+    fse.emptyDirSync(TEST_DIR)
+
+    FIXTURES_DIR = path.join(TEST_DIR, 'fixtures')
+    fse.copySync(SRC_FIXTURES_DIR, FIXTURES_DIR)
+  })
+
+  afterEach(function (done) {
+    rimraf(TEST_DIR, done)
+  })
+
+  it('should rename a file on the same device', function (done) {
+    var src = FIXTURES_DIR + '/a-file'
+    var dest = FIXTURES_DIR + '/a-file-dest'
+
+    fse.move(src, dest, function (err) {
+      assert.ifError(err)
+      fs.readFile(dest, 'utf8', function (err, contents) {
+        var expected = /^sonic the hedgehog\r?\n$/
+        assert.ifError(err)
+        assert.ok(contents.match(expected), contents + ' match ' + expected)
+        done()
+      })
+    })
+  })
+
+  it('should not overwrite if clobber = false', function (done) {
+    var src = FIXTURES_DIR + '/a-file'
+    var dest = FIXTURES_DIR + '/a-folder/another-file'
+
+    // verify file exists already
+    assert(fs.existsSync(dest))
+
+    fse.move(src, dest, {clobber: false}, function (err) {
+      assert.ok(err && err.code === 'EEXIST', 'throw EEXIST')
+      done()
+    })
+  })
+
+  it('should not create directory structure if mkdirp is false', function (done) {
+    var src = FIXTURES_DIR + '/a-file'
+    var dest = FIXTURES_DIR + '/does/not/exist/a-file-dest'
+
+    // verify dest directory does not exist
+    assert(!fs.existsSync(path.dirname(dest)))
+
+    fse.move(src, dest, {mkdirp: false}, function (err) {
+      assert.strictEqual(err.code, 'ENOENT')
+      done()
+    })
+  })
+
+  it('should create directory structure by default', function (done) {
+    var src = FIXTURES_DIR + '/a-file'
+    var dest = FIXTURES_DIR + '/does/not/exist/a-file-dest'
+
+    // verify dest directory does not exist
+    assert(!fs.existsSync(path.dirname(dest)))
+
+    fse.move(src, dest, function (err) {
+      assert.ifError(err)
+      fs.readFile(dest, 'utf8', function (err, contents) {
+        var expected = /^sonic the hedgehog\r?\n$/
+        assert.ifError(err)
+        assert.ok(contents.match(expected), contents + ' match ' + expected)
+        done()
+      })
+    })
+  })
+
+  it('should work across devices', function (done) {
+    var src = FIXTURES_DIR + '/a-file'
+    var dest = FIXTURES_DIR + '/a-file-dest'
+
+    setUpMockFs('EXDEV')
+
+    fse.move(src, dest, function (err) {
+      assert.ifError(err)
+      assert.strictEqual(fs.link.callCount, 1)
+
+      fs.readFile(dest, 'utf8', function (err, contents) {
+        var expected = /^sonic the hedgehog\r?\n$/
+        assert.ifError(err)
+        assert.ok(contents.match(expected), contents + ' match ' + expected)
+
+        tearDownMockFs()
+        done()
+      })
+    })
+  })
+
+  it('should move folders', function (done) {
+    var src = FIXTURES_DIR + '/a-folder'
+    var dest = FIXTURES_DIR + '/a-folder-dest'
+
+    // verify it doesn't exist
+    assert(!fs.existsSync(dest))
+
+    fse.move(src, dest, function (err) {
+      assert.ifError(err)
+      fs.readFile(dest + '/another-file', 'utf8', function (err, contents) {
+        var expected = /^tails\r?\n$/
+        assert.ifError(err)
+        assert.ok(contents.match(expected), contents + ' match ' + expected)
+        done()
+      })
+    })
+  })
+
+  it('should move folders across devices with EISDIR erro', function (done) {
+    var src = FIXTURES_DIR + '/a-folder'
+    var dest = FIXTURES_DIR + '/a-folder-dest'
+
+    setUpMockFs('EISDIR')
+
+    fse.move(src, dest, function (err) {
+      assert.ifError(err)
+      assert.strictEqual(fs.link.callCount, 1)
+
+      fs.readFile(dest + '/another-folder/file3', 'utf8', function (err, contents) {
+        var expected = /^knuckles\r?\n$/
+        assert.ifError(err)
+        assert.ok(contents.match(expected), contents + ' match ' + expected)
+
+        tearDownMockFs('EISDIR')
+
+        done()
+      })
+    })
+  })
+
+  it('should clobber folders across devices', function (done) {
+    var src = FIXTURES_DIR + '/a-folder'
+    var dest = FIXTURES_DIR + '/a-folder-dest'
+
+    fs.mkdirSync(dest)
+
+    setUpMockFs('EXDEV')
+
+    fse.move(src, dest, {clobber: true}, function (err) {
+      assert.ifError(err)
+      assert.strictEqual(fs.rename.callCount, 1)
+
+      fs.readFile(dest + '/another-folder/file3', 'utf8', function (err, contents) {
+        var expected = /^knuckles\r?\n$/
+        assert.ifError(err)
+        assert.ok(contents.match(expected), contents + ' match ' + expected)
+
+        tearDownMockFs('EXDEV')
+
+        done()
+      })
+    })
+  })
+
+  it('should move folders across devices with EXDEV error', function (done) {
+    var src = FIXTURES_DIR + '/a-folder'
+    var dest = FIXTURES_DIR + '/a-folder-dest'
+
+    setUpMockFs('EXDEV')
+
+    fse.move(src, dest, function (err) {
+      assert.ifError(err)
+      assert.strictEqual(fs.link.callCount, 1)
+
+      fs.readFile(dest + '/another-folder/file3', 'utf8', function (err, contents) {
+        var expected = /^knuckles\r?\n$/
+        assert.ifError(err)
+        assert.ok(contents.match(expected), contents + ' match ' + expected)
+
+        tearDownMockFs()
+
+        done()
+      })
+    })
+  })
+
+  describe.skip('> when trying to a move a folder into itself', function () {
+    it('should produce an error', function (done) {
+      var SRC_DIR = path.join(TEST_DIR, 'test')
+      var DEST_DIR = path.join(TEST_DIR, 'test', 'test')
+
+      assert(!fs.existsSync(SRC_DIR))
+      fs.mkdirSync(SRC_DIR)
+      assert(fs.existsSync(SRC_DIR))
+
+      fse.move(SRC_DIR, DEST_DIR, function (err) {
+        assert(fs.existsSync(SRC_DIR))
+        assert(err)
+        done()
+      })
+    })
+  })
+
+  // tested on Linux ubuntu 3.13.0-32-generic #57-Ubuntu SMP i686 i686 GNU/Linux
+  // this won't trigger a bug on Mac OS X Yosimite with a USB drive (/Volumes)
+  // see issue #108
+  describe('> when actually trying to a move a folder across devices', function () {
+    var differentDevice = '/mnt'
+    var __skipTests = false
+
+    // must set this up, if not, exit silently
+    if (!fs.existsSync(differentDevice)) {
+      console.log('Skipping cross-device move test')
+      __skipTests = true
+    }
+
+    // make sure we have permission on device
+    try {
+      fs.writeFileSync(path.join(differentDevice, 'file'), 'hi')
+    } catch (err) {
+      console.log("Can't write to device. Skipping test.")
+      __skipTests = true
+    }
+
+    var _it = __skipTests ? it.skip : it
+
+    describe('> just the folder', function () {
+      _it('should move the folder', function (done) {
+        var src = '/mnt/some/weird/dir-really-weird'
+        var dest = path.join(TEST_DIR, 'device-weird')
+
+        if (!fs.existsSync(src)) {
+          fse.mkdirpSync(src)
+        }
+
+        assert(!fs.existsSync(dest))
+
+        assert(fs.lstatSync(src).isDirectory())
+
+        fse.move(src, dest, function (err) {
+          assert.ifError(err)
+          assert(fs.existsSync(dest))
+          // console.log(path.normalize(dest))
+          assert(fs.lstatSync(dest).isDirectory())
+          done()
+        })
+      })
+    })
+  })
+})
diff --git a/lib/move.js b/lib/move/index.js
similarity index 50%
rename from lib/move.js
rename to lib/move/index.js
index 08320fd..1eb2a14 100644
--- a/lib/move.js
+++ b/lib/move/index.js
@@ -5,19 +5,20 @@
 // this needs a cleanup
 
 var fs = require('graceful-fs')
-var ncp = require('./_copy').ncp
+var ncp = require('../copy/ncp')
 var path = require('path')
-var rimraf = require('rimraf')
-var mkdirp = require('./mkdir').mkdirs
+var remove = require('../remove').remove
+var mkdirp = require('../mkdirs').mkdirs
 
-function mv(source, dest, options, callback){
+function mv (source, dest, options, callback) {
   if (typeof options === 'function') {
     callback = options
     options = {}
   }
 
-  var shouldMkdirp = !!options.mkdirp
-  var clobber = options.clobber !== false
+  var shouldMkdirp = ('mkdirp' in options) ? options.mkdirp : true
+  var clobber = ('clobber' in options) ? options.clobber : false
+
   var limit = options.limit || 16
 
   if (shouldMkdirp) {
@@ -26,20 +27,20 @@ function mv(source, dest, options, callback){
     doRename()
   }
 
-  function mkdirs() {
-    mkdirp(path.dirname(dest), function(err) {
+  function mkdirs () {
+    mkdirp(path.dirname(dest), function (err) {
       if (err) return callback(err)
       doRename()
     })
   }
 
-  function doRename() {
+  function doRename () {
     if (clobber) {
-      fs.rename(source, dest, function(err) {
+      fs.rename(source, dest, function (err) {
         if (!err) return callback()
 
-        if (err.code === 'ENOTEMPTY') {
-          rimraf(dest, function(err) {
+        if (err.code === 'ENOTEMPTY' || err.code === 'EEXIST') {
+          remove(dest, function (err) {
             if (err) return callback(err)
             options.clobber = false // just clobbered it, no need to do it again
             mv(source, dest, options, callback)
@@ -47,18 +48,26 @@ function mv(source, dest, options, callback){
           return
         }
 
+        // weird Windows shit
+        if (err.code === 'EPERM') {
+          setTimeout(function () {
+            remove(dest, function (err) {
+              if (err) return callback(err)
+              options.clobber = false
+              mv(source, dest, options, callback)
+            })
+          }, 200)
+          return
+        }
+
         if (err.code !== 'EXDEV') return callback(err)
-        moveFileAcrossDevice(source, dest, clobber, limit, callback)
+        moveAcrossDevice(source, dest, clobber, limit, callback)
       })
     } else {
-      fs.link(source, dest, function(err) {
+      fs.link(source, dest, function (err) {
         if (err) {
-          if (err.code === 'EXDEV') {
-            moveFileAcrossDevice(source, dest, clobber, limit, callback)
-            return
-          }
-          if (err.code === 'EISDIR' || err.code === 'EPERM') {
-            moveDirAcrossDevice(source, dest, clobber, limit, callback)
+          if (err.code === 'EXDEV' || err.code === 'EISDIR' || err.code === 'EPERM') {
+            moveAcrossDevice(source, dest, clobber, limit, callback)
             return
           }
           callback(err)
@@ -70,12 +79,27 @@ function mv(source, dest, options, callback){
   }
 }
 
-function moveFileAcrossDevice(source, dest, clobber, limit, callback) {
+function moveAcrossDevice (source, dest, clobber, limit, callback) {
+  fs.stat(source, function (err, stat) {
+    if (err) {
+      callback(err)
+      return
+    }
+
+    if (stat.isDirectory()) {
+      moveDirAcrossDevice(source, dest, clobber, limit, callback)
+    } else {
+      moveFileAcrossDevice(source, dest, clobber, limit, callback)
+    }
+  })
+}
+
+function moveFileAcrossDevice (source, dest, clobber, limit, callback) {
   var outFlags = clobber ? 'w' : 'wx'
   var ins = fs.createReadStream(source)
   var outs = fs.createWriteStream(dest, {flags: outFlags})
 
-  ins.on('error', function(err) {
+  ins.on('error', function (err) {
     ins.destroy()
     outs.destroy()
     outs.removeListener('close', onClose)
@@ -83,7 +107,7 @@ function moveFileAcrossDevice(source, dest, clobber, limit, callback) {
     // may want to create a directory but `out` line above
     // creates an empty file for us: See #108
     // don't care about error here
-    fs.unlink(dest, function() {
+    fs.unlink(dest, function () {
       // note: `err` here is from the input stream errror
       if (err.code === 'EISDIR' || err.code === 'EPERM') {
         moveDirAcrossDevice(source, dest, clobber, limit, callback)
@@ -93,7 +117,7 @@ function moveFileAcrossDevice(source, dest, clobber, limit, callback) {
     })
   })
 
-  outs.on('error', function(err) {
+  outs.on('error', function (err) {
     ins.destroy()
     outs.destroy()
     outs.removeListener('close', onClose)
@@ -103,27 +127,27 @@ function moveFileAcrossDevice(source, dest, clobber, limit, callback) {
   outs.once('close', onClose)
   ins.pipe(outs)
 
-  function onClose() {
+  function onClose () {
     fs.unlink(source, callback)
   }
 }
 
-function moveDirAcrossDevice(source, dest, clobber, limit, callback) {
+function moveDirAcrossDevice (source, dest, clobber, limit, callback) {
   var options = {
     stopOnErr: true,
     clobber: false,
-    limit: limit,
+    limit: limit
   }
 
-  function startNcp() {
-    ncp(source, dest, options, function(errList) {
+  function startNcp () {
+    ncp(source, dest, options, function (errList) {
       if (errList) return callback(errList[0])
-      rimraf(source, callback)
+      remove(source, callback)
     })
   }
 
   if (clobber) {
-    rimraf(dest, function(err) {
+    remove(dest, function (err) {
       if (err) return callback(err)
       startNcp()
     })
@@ -132,5 +156,6 @@ function moveDirAcrossDevice(source, dest, clobber, limit, callback) {
   }
 }
 
-module.exports = mv
-
+module.exports = {
+  move: mv
+}
diff --git a/test/output.test.js b/lib/output/__tests__/output.test.js
similarity index 66%
rename from test/output.test.js
rename to lib/output/__tests__/output.test.js
index b97cf5d..e1a857b 100644
--- a/test/output.test.js
+++ b/lib/output/__tests__/output.test.js
@@ -1,28 +1,29 @@
 var assert = require('assert')
 var fs = require('fs')
 var path = require('path')
-var testutil = require('testutil')
-var fse = require('../')
+var os = require('os')
+var fse = require(process.cwd())
 
 /* global afterEach, beforeEach, describe, it */
 
-var TEST_DIR = ''
-
 describe('output', function () {
-  beforeEach(function() {
-    TEST_DIR = testutil.createTestDir('fs-extra')
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'output')
+    fse.emptyDir(TEST_DIR, done)
   })
 
-  afterEach(function(done) {
+  afterEach(function (done) {
     fse.remove(TEST_DIR, done)
   })
 
-  describe('+ outputFile', function() {
-    describe('> when the file and directory does not exist', function() {
-      it('should create the file', function(done) {
+  describe('+ outputFile', function () {
+    describe('> when the file and directory does not exist', function () {
+      it('should create the file', function (done) {
         var file = path.join(TEST_DIR, Math.random() + 't-ne', Math.random() + '.txt')
         assert(!fs.existsSync(file))
-        fse.outputFile(file, 'hi jp', function(err) {
+        fse.outputFile(file, 'hi jp', function (err) {
           assert.ifError(err)
           assert(fs.existsSync(file))
           assert.equal(fs.readFileSync(file, 'utf8'), 'hi jp')
@@ -31,12 +32,12 @@ describe('output', function () {
       })
     })
 
-    describe('> when the file does exist', function() {
-      it('should still modify the file', function(done) {
+    describe('> when the file does exist', function () {
+      it('should still modify the file', function (done) {
         var file = path.join(TEST_DIR, Math.random() + 't-e', Math.random() + '.txt')
         fse.mkdirsSync(path.dirname(file))
         fs.writeFileSync(file, 'hello world')
-        fse.outputFile(file, 'hello jp', function(err) {
+        fse.outputFile(file, 'hello jp', function (err) {
           if (err) return done(err)
           assert.equal(fs.readFileSync(file, 'utf8'), 'hello jp')
           done()
@@ -45,9 +46,9 @@ describe('output', function () {
     })
   })
 
-  describe('+ outputFileSync', function() {
-    describe('> when the file and directory does not exist', function() {
-      it('should create the file', function() {
+  describe('+ outputFileSync', function () {
+    describe('> when the file and directory does not exist', function () {
+      it('should create the file', function () {
         var file = path.join(TEST_DIR, Math.random() + 'ts-ne', Math.random() + '.txt')
         assert(!fs.existsSync(file))
         fse.outputFileSync(file, 'hello man')
@@ -56,8 +57,8 @@ describe('output', function () {
       })
     })
 
-    describe('> when the file does exist', function() {
-      it('should still modify the file', function() {
+    describe('> when the file does exist', function () {
+      it('should still modify the file', function () {
         var file = path.join(TEST_DIR, Math.random() + 'ts-e', Math.random() + '.txt')
         fse.mkdirsSync(path.dirname(file))
         fs.writeFileSync(file, 'hello world')
diff --git a/lib/output.js b/lib/output/index.js
similarity index 82%
rename from lib/output.js
rename to lib/output/index.js
index eb9ec3c..e8f45f3 100644
--- a/lib/output.js
+++ b/lib/output/index.js
@@ -1,6 +1,6 @@
 var path = require('path')
 var fs = require('graceful-fs')
-var mkdir = require('./mkdir')
+var mkdir = require('../mkdirs')
 
 function outputFile (file, data, encoding, callback) {
   if (typeof encoding === 'function') {
@@ -9,12 +9,12 @@ function outputFile (file, data, encoding, callback) {
   }
 
   var dir = path.dirname(file)
-  fs.exists(dir, function(itDoes) {
+  fs.exists(dir, function (itDoes) {
     if (itDoes) return fs.writeFile(file, data, encoding, callback)
-    
-    mkdir.mkdirs(dir, function(err) {
+
+    mkdir.mkdirs(dir, function (err) {
       if (err) return callback(err)
-      
+
       fs.writeFile(file, data, encoding, callback)
     })
   })
@@ -22,8 +22,9 @@ function outputFile (file, data, encoding, callback) {
 
 function outputFileSync (file, data, encoding) {
   var dir = path.dirname(file)
-  if (fs.existsSync(dir)) 
+  if (fs.existsSync(dir)) {
     return fs.writeFileSync.apply(fs, arguments)
+  }
   mkdir.mkdirsSync(dir)
   fs.writeFileSync.apply(fs, arguments)
 }
diff --git a/lib/remove.js b/lib/remove.js
deleted file mode 100644
index d6e3876..0000000
--- a/lib/remove.js
+++ /dev/null
@@ -1,14 +0,0 @@
-var rimraf = require('rimraf')
-
-function removeSync(dir) {
-  return rimraf.sync(dir)
-}
-
-function remove(dir, callback) {
-  return callback ? rimraf(dir, callback) : rimraf(dir, function(){})
-}
-
-module.exports = {
-  remove: remove,
-  removeSync: removeSync
-}
diff --git a/lib/remove/__tests__/remove-dir.test.js b/lib/remove/__tests__/remove-dir.test.js
new file mode 100644
index 0000000..0b0f5be
--- /dev/null
+++ b/lib/remove/__tests__/remove-dir.test.js
@@ -0,0 +1,27 @@
+var assert = require('assert')
+var fs = require('fs')
+var path = require('path')
+var os = require('os')
+var fse = require(process.cwd())
+
+/* global beforeEach, describe, it */
+
+describe('remove / async / dir', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'remove-async-dir')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  describe('> when dir does not exist', function () {
+    it('should not throw an error', function (done) {
+      var someDir = path.join(TEST_DIR, 'some-dir/')
+      assert.equal(fs.existsSync(someDir), false)
+      fse.remove(someDir, function (err) {
+        assert.ifError(err)
+        done()
+      })
+    })
+  })
+})
diff --git a/lib/remove/__tests__/remove-file.test.js b/lib/remove/__tests__/remove-file.test.js
new file mode 100644
index 0000000..65b3dba
--- /dev/null
+++ b/lib/remove/__tests__/remove-file.test.js
@@ -0,0 +1 @@
+// todo
diff --git a/lib/remove/__tests__/remove-sync-dir.test.js b/lib/remove/__tests__/remove-sync-dir.test.js
new file mode 100644
index 0000000..7469039
--- /dev/null
+++ b/lib/remove/__tests__/remove-sync-dir.test.js
@@ -0,0 +1,31 @@
+var assert = require('assert')
+var fs = require('fs')
+var os = require('os')
+var path = require('path')
+var fse = require(process.cwd())
+
+/* global beforeEach, describe, it */
+
+describe('remove/sync', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'remove-sync')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  describe('+ removeSync()', function () {
+    it('should delete directories and files synchronously', function () {
+      assert(fs.existsSync(TEST_DIR))
+      fs.writeFileSync(path.join(TEST_DIR, 'somefile'), 'somedata')
+      fse.removeSync(TEST_DIR)
+      assert(!fs.existsSync(TEST_DIR))
+    })
+
+    it('should delete an empty directory synchronously', function () {
+      assert(fs.existsSync(TEST_DIR))
+      fse.removeSync(TEST_DIR)
+      assert(!fs.existsSync(TEST_DIR))
+    })
+  })
+})
diff --git a/lib/remove/__tests__/remove-sync-file.test.js b/lib/remove/__tests__/remove-sync-file.test.js
new file mode 100644
index 0000000..5a4bed7
--- /dev/null
+++ b/lib/remove/__tests__/remove-sync-file.test.js
@@ -0,0 +1,26 @@
+var assert = require('assert')
+var fs = require('fs')
+var os = require('os')
+var path = require('path')
+var fse = require(process.cwd())
+
+/* global beforeEach, describe, it */
+
+describe('remove/sync', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'remove-sync')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  describe('+ removeSync()', function () {
+    it('should delete a file synchronously', function () {
+      var file = path.join(TEST_DIR, 'file')
+      fs.writeFileSync(file, 'hello')
+      assert(fs.existsSync(file))
+      fse.removeSync(file)
+      assert(!fs.existsSync(file))
+    })
+  })
+})
diff --git a/lib/remove/__tests__/remove.test.js b/lib/remove/__tests__/remove.test.js
new file mode 100644
index 0000000..dd56381
--- /dev/null
+++ b/lib/remove/__tests__/remove.test.js
@@ -0,0 +1,84 @@
+var assert = require('assert')
+var fs = require('fs')
+var os = require('os')
+var path = require('path')
+var sr = require('secure-random')
+var fse = require(process.cwd())
+
+/* global afterEach, beforeEach, describe, it */
+
+var TEST_DIR
+
+function buildFixtureDir () {
+  var buf = sr.randomBuffer(5)
+  var baseDir = path.join(TEST_DIR, 'TEST_fs-extra_remove-' + Date.now())
+
+  fs.mkdirSync(baseDir)
+  fs.writeFileSync(path.join(baseDir, Math.random() + ''), buf)
+  fs.writeFileSync(path.join(baseDir, Math.random() + ''), buf)
+
+  var subDir = path.join(TEST_DIR, Math.random() + '')
+  fs.mkdirSync(subDir)
+  fs.writeFileSync(path.join(subDir, Math.random() + ''))
+  return baseDir
+}
+
+describe('remove', function () {
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'remove')
+    fse.emptyDir(TEST_DIR, done)
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  describe('+ remove()', function () {
+    it('should delete an empty directory', function (done) {
+      assert(fs.existsSync(TEST_DIR))
+      fse.remove(TEST_DIR, function (err) {
+        assert.ifError(err)
+        assert(!fs.existsSync(TEST_DIR))
+        done()
+      })
+    })
+
+    it('should delete a directory full of directories and files', function (done) {
+      buildFixtureDir()
+      assert(fs.existsSync(TEST_DIR))
+      fse.remove(TEST_DIR, function (err) {
+        assert.ifError(err)
+        assert(!fs.existsSync(TEST_DIR))
+        done()
+      })
+    })
+
+    it('should delete a file', function (done) {
+      var file = path.join(TEST_DIR, 'file')
+      fs.writeFileSync(file, 'hello')
+
+      assert(fs.existsSync(file))
+      fse.remove(file, function (err) {
+        assert.ifError(err)
+        assert(!fs.existsSync(file))
+        done()
+      })
+    })
+
+    it('should delete without a callback', function (done) {
+      var file = path.join(TEST_DIR, 'file')
+      fs.writeFileSync(file, 'hello')
+
+      assert(fs.existsSync(file))
+      var existsChecker = setInterval(function () {
+        fs.exists(file, function (itDoes) {
+          if (!itDoes) {
+            clearInterval(existsChecker)
+            done()
+          }
+        })
+      }, 25)
+      fse.remove(file)
+    })
+  })
+})
diff --git a/lib/remove/index.js b/lib/remove/index.js
new file mode 100644
index 0000000..566b866
--- /dev/null
+++ b/lib/remove/index.js
@@ -0,0 +1,15 @@
+var rimraf = require('./rimraf')
+
+function removeSync (dir) {
+  return rimraf.sync(dir, {disableGlob: true})
+}
+
+function remove (dir, callback) {
+  var options = {disableGlob: true}
+  return callback ? rimraf(dir, options, callback) : rimraf(dir, options, function () {})
+}
+
+module.exports = {
+  remove: remove,
+  removeSync: removeSync
+}
diff --git a/lib/remove/rimraf.js b/lib/remove/rimraf.js
new file mode 100644
index 0000000..da55a62
--- /dev/null
+++ b/lib/remove/rimraf.js
@@ -0,0 +1,301 @@
+module.exports = rimraf
+rimraf.sync = rimrafSync
+
+var assert = require('assert')
+var path = require('path')
+var fs = require('graceful-fs')
+
+var isWindows = (process.platform === 'win32')
+
+function defaults (options) {
+  var methods = [
+    'unlink',
+    'chmod',
+    'stat',
+    'lstat',
+    'rmdir',
+    'readdir'
+  ]
+  methods.forEach(function (m) {
+    options[m] = options[m] || fs[m]
+    m = m + 'Sync'
+    options[m] = options[m] || fs[m]
+  })
+
+  options.maxBusyTries = options.maxBusyTries || 3
+}
+
+function rimraf (p, options, cb) {
+  if (typeof options === 'function') {
+    cb = options
+    options = {}
+  }
+
+  assert(p, 'rimraf: missing path')
+  assert.equal(typeof p, 'string', 'rimraf: path should be a string')
+  assert.equal(typeof cb, 'function', 'rimraf: callback function required')
+  assert(options, 'rimraf: invalid options argument provided')
+  assert.equal(typeof options, 'object', 'rimraf: options should be object')
+
+  defaults(options)
+
+  var busyTries = 0
+
+  rimraf_(p, options, function CB (er) {
+    if (er) {
+      if (isWindows && (er.code === 'EBUSY' || er.code === 'ENOTEMPTY' || er.code === 'EPERM') &&
+          busyTries < options.maxBusyTries) {
+        busyTries++
+        var time = busyTries * 100
+        // try again, with the same exact callback as this one.
+        return setTimeout(function () {
+          rimraf_(p, options, CB)
+        }, time)
+      }
+
+      // already gone
+      if (er.code === 'ENOENT') er = null
+    }
+
+    cb(er)
+  })
+}
+
+// Two possible strategies.
+// 1. Assume it's a file.  unlink it, then do the dir stuff on EPERM or EISDIR
+// 2. Assume it's a directory.  readdir, then do the file stuff on ENOTDIR
+//
+// Both result in an extra syscall when you guess wrong.  However, there
+// are likely far more normal files in the world than directories.  This
+// is based on the assumption that a the average number of files per
+// directory is >= 1.
+//
+// If anyone ever complains about this, then I guess the strategy could
+// be made configurable somehow.  But until then, YAGNI.
+function rimraf_ (p, options, cb) {
+  assert(p)
+  assert(options)
+  assert(typeof cb === 'function')
+
+  // sunos lets the root user unlink directories, which is... weird.
+  // so we have to lstat here and make sure it's not a dir.
+  options.lstat(p, function (er, st) {
+    if (er && er.code === 'ENOENT') {
+      return cb(null)
+    }
+
+    // Windows can EPERM on stat.  Life is suffering.
+    if (er && er.code === 'EPERM' && isWindows) {
+      fixWinEPERM(p, options, er, cb)
+    }
+
+    if (st && st.isDirectory()) {
+      return rmdir(p, options, er, cb)
+    }
+
+    options.unlink(p, function (er) {
+      if (er) {
+        if (er.code === 'ENOENT') {
+          return cb(null)
+        }
+        if (er.code === 'EPERM') {
+          return (isWindows)
+            ? fixWinEPERM(p, options, er, cb)
+            : rmdir(p, options, er, cb)
+        }
+        if (er.code === 'EISDIR') {
+          return rmdir(p, options, er, cb)
+        }
+      }
+      return cb(er)
+    })
+  })
+}
+
+function fixWinEPERM (p, options, er, cb) {
+  assert(p)
+  assert(options)
+  assert(typeof cb === 'function')
+  if (er) {
+    assert(er instanceof Error)
+  }
+
+  options.chmod(p, 666, function (er2) {
+    if (er2) {
+      cb(er2.code === 'ENOENT' ? null : er)
+    } else {
+      options.stat(p, function (er3, stats) {
+        if (er3) {
+          cb(er3.code === 'ENOENT' ? null : er)
+        } else if (stats.isDirectory()) {
+          rmdir(p, options, er, cb)
+        } else {
+          options.unlink(p, cb)
+        }
+      })
+    }
+  })
+}
+
+function fixWinEPERMSync (p, options, er) {
+  assert(p)
+  assert(options)
+  if (er) {
+    assert(er instanceof Error)
+  }
+
+  try {
+    options.chmodSync(p, 666)
+  } catch (er2) {
+    if (er2.code === 'ENOENT') {
+      return
+    } else {
+      throw er
+    }
+  }
+
+  try {
+    var stats = options.statSync(p)
+  } catch (er3) {
+    if (er3.code === 'ENOENT') {
+      return
+    } else {
+      throw er
+    }
+  }
+
+  if (stats.isDirectory()) {
+    rmdirSync(p, options, er)
+  } else {
+    options.unlinkSync(p)
+  }
+}
+
+function rmdir (p, options, originalEr, cb) {
+  assert(p)
+  assert(options)
+  if (originalEr) {
+    assert(originalEr instanceof Error)
+  }
+  assert(typeof cb === 'function')
+
+  // try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
+  // if we guessed wrong, and it's not a directory, then
+  // raise the original error.
+  options.rmdir(p, function (er) {
+    if (er && (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM')) {
+      rmkids(p, options, cb)
+    } else if (er && er.code === 'ENOTDIR') {
+      cb(originalEr)
+    } else {
+      cb(er)
+    }
+  })
+}
+
+function rmkids (p, options, cb) {
+  assert(p)
+  assert(options)
+  assert(typeof cb === 'function')
+
+  options.readdir(p, function (er, files) {
+    if (er) {
+      return cb(er)
+    }
+    var n = files.length
+    if (n === 0) {
+      return options.rmdir(p, cb)
+    }
+    var errState
+    files.forEach(function (f) {
+      rimraf(path.join(p, f), options, function (er) {
+        if (errState) {
+          return
+        }
+        if (er) {
+          return cb(errState = er)
+        }
+        if (--n === 0) {
+          options.rmdir(p, cb)
+        }
+      })
+    })
+  })
+}
+
+// this looks simpler, and is strictly *faster*, but will
+// tie up the JavaScript thread and fail on excessively
+// deep directory trees.
+function rimrafSync (p, options) {
+  options = options || {}
+  defaults(options)
+
+  assert(p, 'rimraf: missing path')
+  assert.equal(typeof p, 'string', 'rimraf: path should be a string')
+  assert(options, 'rimraf: missing options')
+  assert.equal(typeof options, 'object', 'rimraf: options should be object')
+
+  try {
+    var st = options.lstatSync(p)
+  } catch (er) {
+    if (er.code === 'ENOENT') {
+      return
+    }
+
+    // Windows can EPERM on stat.  Life is suffering.
+    if (er.code === 'EPERM' && isWindows) {
+      fixWinEPERMSync(p, options, er)
+    }
+  }
+
+  try {
+    // sunos lets the root user unlink directories, which is... weird.
+    if (st && st.isDirectory()) {
+      rmdirSync(p, options, null)
+    } else {
+      options.unlinkSync(p)
+    }
+  } catch (er) {
+    if (er.code === 'ENOENT') {
+      return
+    }
+    if (er.code === 'EPERM') {
+      return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
+    }
+    if (er.code !== 'EISDIR') {
+      throw er
+    }
+    rmdirSync(p, options, er)
+  }
+}
+
+function rmdirSync (p, options, originalEr) {
+  assert(p)
+  assert(options)
+  if (originalEr) {
+    assert(originalEr instanceof Error)
+  }
+
+  try {
+    options.rmdirSync(p)
+  } catch (er) {
+    if (er.code === 'ENOENT') {
+      return
+    }
+    if (er.code === 'ENOTDIR') {
+      throw originalEr
+    }
+    if (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM') {
+      rmkidsSync(p, options)
+    }
+  }
+}
+
+function rmkidsSync (p, options) {
+  assert(p)
+  assert(options)
+  options.readdirSync(p).forEach(function (f) {
+    rimrafSync(path.join(p, f), options)
+  })
+  options.rmdirSync(p, options)
+}
diff --git a/lib/util/__tests__/utimes.test.js b/lib/util/__tests__/utimes.test.js
new file mode 100644
index 0000000..648f424
--- /dev/null
+++ b/lib/util/__tests__/utimes.test.js
@@ -0,0 +1,122 @@
+var assert = require('assert')
+var path = require('path')
+var os = require('os')
+var fs = require('fs')
+var fse = require(process.cwd())
+var semver = require('semver')
+var proxyquire = require('proxyquire')
+var gracefulFsStub
+var utimes
+
+/* global beforeEach, describe, it */
+
+describe('utimes', function () {
+  var TEST_DIR
+
+  // ignore Node.js v0.10.x
+  if (semver.lt(process.version, '0.11.0')) return
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'utimes')
+    fse.emptyDir(TEST_DIR, done)
+    // reset stubs
+    gracefulFsStub = {}
+    utimes = proxyquire('../utimes', {'graceful-fs': gracefulFsStub})
+  })
+
+  describe('hasMillisResSync()', function () {
+    it('should return a boolean indicating whether it has support', function () {
+      var res = utimes.hasMillisResSync()
+      assert.equal(typeof res, 'boolean')
+
+      // HFS => false
+      if (process.platform === 'darwin') {
+        assert.equal(res, false)
+      }
+
+      // does anyone use FAT anymore?
+      /* if (process.platform === 'win32') {
+        assert.equal(res, true)
+      } */
+      // fails on appveyor... could appveyor be using FAT?
+
+      // this would fail if ext2/ext3
+      if (process.platform === 'linux') {
+        assert.equal(res, true)
+      }
+    })
+  })
+
+  describe('timeRemoveMills()', function () {
+    it('should remove millisecond precision from a timestamp', function () {
+      var ts = 1334990868773
+      var ets = 1334990868000
+      assert.strictEqual(utimes.timeRemoveMillis(ts), ets)
+      assert.strictEqual(utimes.timeRemoveMillis(new Date(ts)).getTime(), ets)
+    })
+  })
+
+  describe('utimesMillis()', function () {
+    // see discussion https://github.com/jprichardson/node-fs-extra/pull/141
+    it('should set the utimes w/ millisecond precision', function (done) {
+      var tmpFile = path.join(TEST_DIR, 'someFile')
+      fs.writeFileSync(tmpFile, 'hello')
+
+      var stats = fs.lstatSync(tmpFile)
+
+      // Apr 21st, 2012
+      var awhileAgo = new Date(1334990868773)
+      var awhileAgoNoMillis = new Date(1334990868000)
+
+      assert.notDeepEqual(stats.mtime, awhileAgo)
+      assert.notDeepEqual(stats.atime, awhileAgo)
+
+      utimes.utimesMillis(tmpFile, awhileAgo, awhileAgo, function (err) {
+        assert.ifError(err)
+        stats = fs.statSync(tmpFile)
+        if (utimes.hasMillisResSync()) {
+          assert.deepEqual(stats.mtime, awhileAgo)
+          assert.deepEqual(stats.atime, awhileAgo)
+        } else {
+          assert.deepEqual(stats.mtime, awhileAgoNoMillis)
+          assert.deepEqual(stats.atime, awhileAgoNoMillis)
+        }
+        done()
+      })
+    })
+
+    it('should close open file desciptors after encountering an error', function (done) {
+      var fakeFd = Math.random()
+
+      gracefulFsStub.open = function (pathIgnored, flagsIgnored, modeIgnored, callback) {
+        if (typeof modeIgnored === 'function') {
+          callback = modeIgnored
+        }
+        process.nextTick(function () {
+          callback(null, fakeFd)
+        })
+      }
+
+      var closeCalled = false
+      gracefulFsStub.close = function (fd, callback) {
+        assert.equal(fd, fakeFd)
+        closeCalled = true
+        if (callback) process.nextTick(callback)
+      }
+
+      var testError
+      gracefulFsStub.futimes = function (fd, atimeIgnored, mtimeIgnored, callback) {
+        process.nextTick(function () {
+          testError = new Error('A test error')
+          callback(testError)
+        })
+      }
+
+      utimes.utimesMillis('ignored', 'ignored', 'ignored', function (err) {
+        assert.equal(err, testError)
+        assert(closeCalled)
+        done()
+      })
+    })
+  })
+})
diff --git a/lib/util/assign.js b/lib/util/assign.js
new file mode 100644
index 0000000..8e41f9a
--- /dev/null
+++ b/lib/util/assign.js
@@ -0,0 +1,14 @@
+// simple mutable assign
+function assign () {
+  var args = [].slice.call(arguments).filter(function (i) { return i })
+  var dest = args.shift()
+  args.forEach(function (src) {
+    Object.keys(src).forEach(function (key) {
+      dest[key] = src[key]
+    })
+  })
+
+  return dest
+}
+
+module.exports = assign
diff --git a/lib/util/utimes.js b/lib/util/utimes.js
new file mode 100644
index 0000000..118f8a7
--- /dev/null
+++ b/lib/util/utimes.js
@@ -0,0 +1,70 @@
+var fs = require('graceful-fs')
+var path = require('path')
+var os = require('os')
+
+// HFS, ext{2,3}, FAT do not, Node.js v0.10 does not
+function hasMillisResSync () {
+  var tmpfile = path.join('millis-test-sync' + Date.now().toString() + Math.random().toString().slice(2))
+  tmpfile = path.join(os.tmpdir(), tmpfile)
+
+  // 550 millis past UNIX epoch
+  var d = new Date(1435410243862)
+  fs.writeFileSync(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141')
+  var fd = fs.openSync(tmpfile, 'r+')
+  fs.futimesSync(fd, d, d)
+  fs.closeSync(fd)
+  return fs.statSync(tmpfile).mtime > 1435410243000
+}
+
+function hasMillisRes (callback) {
+  var tmpfile = path.join('millis-test' + Date.now().toString() + Math.random().toString().slice(2))
+  tmpfile = path.join(os.tmpdir(), tmpfile)
+
+  // 550 millis past UNIX epoch
+  var d = new Date(1435410243862)
+  fs.writeFile(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141', function (err) {
+    if (err) return callback(err)
+    fs.open(tmpfile, 'r+', function (err, fd) {
+      if (err) return callback(err)
+      fs.futimes(fd, d, d, function (err) {
+        if (err) return callback(err)
+        fs.close(fd, function (err) {
+          if (err) return callback(err)
+          fs.stat(tmpfile, function (err, stats) {
+            if (err) return callback(err)
+            callback(null, stats.mtime > 1435410243000)
+          })
+        })
+      })
+    })
+  })
+}
+
+function timeRemoveMillis (timestamp) {
+  if (typeof timestamp === 'number') {
+    return Math.floor(timestamp / 1000) * 1000
+  } else if (timestamp instanceof Date) {
+    return new Date(Math.floor(timestamp.getTime() / 1000) * 1000)
+  } else {
+    throw new Error('fs-extra: timeRemoveMillis() unknown parameter type')
+  }
+}
+
+function utimesMillis (path, atime, mtime, callback) {
+  // if (!HAS_MILLIS_RES) return fs.utimes(path, atime, mtime, callback)
+  fs.open(path, 'r+', function (err, fd) {
+    if (err) return callback(err)
+    fs.futimes(fd, atime, mtime, function (futimesErr) {
+      fs.close(fd, function (closeErr) {
+        if (callback) callback(futimesErr || closeErr)
+      })
+    })
+  })
+}
+
+module.exports = {
+  hasMillisRes: hasMillisRes,
+  hasMillisResSync: hasMillisResSync,
+  timeRemoveMillis: timeRemoveMillis,
+  utimesMillis: utimesMillis
+}
diff --git a/test/ncp/fixtures/regular-fixtures/out/c b/lib/walk-sync/__tests__/fixtures/dir1/file1_2
similarity index 100%
copy from test/ncp/fixtures/regular-fixtures/out/c
copy to lib/walk-sync/__tests__/fixtures/dir1/file1_2
diff --git a/test/ncp/fixtures/regular-fixtures/out/c b/lib/walk-sync/__tests__/fixtures/dir2/dir2_1/file2_1_1
similarity index 100%
copy from test/ncp/fixtures/regular-fixtures/out/c
copy to lib/walk-sync/__tests__/fixtures/dir2/dir2_1/file2_1_1
diff --git a/test/ncp/fixtures/regular-fixtures/out/c b/lib/walk-sync/__tests__/fixtures/file1
similarity index 100%
rename from test/ncp/fixtures/regular-fixtures/out/c
rename to lib/walk-sync/__tests__/fixtures/file1
diff --git a/lib/walk-sync/__tests__/walkSync.test.js b/lib/walk-sync/__tests__/walkSync.test.js
new file mode 100644
index 0000000..ee3b867
--- /dev/null
+++ b/lib/walk-sync/__tests__/walkSync.test.js
@@ -0,0 +1,41 @@
+var assert = require('assert')
+var path = require('path')
+
+var fse = require('../../')
+
+/* global describe, it */
+var fixturesDir = path.join(__dirname, 'fixtures')
+
+describe('walk-sync', function () {
+  it('should return an error if the source dir does not exist', function (done) {
+    try {
+      fse.walkSync('dirDoesNotExist/')
+    } catch (err) {
+      assert.equal(err.code, 'ENOENT')
+    } finally {
+      done()
+    }
+  })
+
+  it('should return an error if the source is not a dir', function (done) {
+    try {
+      fse.walkSync(path.join(fixturesDir, 'dir1/file1_2'))
+    } catch (err) {
+      assert.equal(err.code, 'ENOTDIR')
+    } finally {
+      done()
+    }
+  })
+
+  it('should return all files successfully for a dir', function (done) {
+    var files = fse.walkSync(fixturesDir)
+    var expectedFiles = ['dir1/file1_2', 'dir2/dir2_1/file2_1_1', 'file1']
+    expectedFiles = expectedFiles.map(function (item) {
+      return path.join(fixturesDir, item)
+    })
+    files.forEach(function (elem, i) {
+      assert.equal(elem, expectedFiles[i])
+    })
+    done()
+  })
+})
diff --git a/lib/walk-sync/index.js b/lib/walk-sync/index.js
new file mode 100644
index 0000000..0ebe56e
--- /dev/null
+++ b/lib/walk-sync/index.js
@@ -0,0 +1,20 @@
+var fs = require('graceful-fs')
+var path = require('path')
+
+var walkSync = function (dir, filelist) {
+  var files = fs.readdirSync(dir)
+  filelist = filelist || []
+  files.forEach(function (file) {
+    var nestedPath = path.join(dir, file)
+    if (fs.lstatSync(nestedPath).isDirectory()) {
+      filelist = walkSync(nestedPath, filelist)
+    } else {
+      filelist.push(nestedPath)
+    }
+  })
+  return filelist
+}
+
+module.exports = {
+  walkSync: walkSync
+}
diff --git a/lib/walk/__tests__/fixtures.json b/lib/walk/__tests__/fixtures.json
new file mode 100644
index 0000000..8f5df49
--- /dev/null
+++ b/lib/walk/__tests__/fixtures.json
@@ -0,0 +1,7 @@
+[
+  "a/b/c/d.txt",
+  "a/e.jpg",
+  "h/i/j/k.txt",
+  "h/i/l.txt",
+  "h/i/m.jpg"
+]
diff --git a/lib/walk/__tests__/walk.test.js b/lib/walk/__tests__/walk.test.js
new file mode 100644
index 0000000..5840283
--- /dev/null
+++ b/lib/walk/__tests__/walk.test.js
@@ -0,0 +1,73 @@
+var assert = require('assert')
+var path = require('path')
+var os = require('os')
+var fse = require('../../')
+var fixtures = require('./fixtures')
+
+/* global afterEach, beforeEach, describe, it */
+// trinity: mocha
+
+describe('walk()', function () {
+  var TEST_DIR
+
+  beforeEach(function (done) {
+    TEST_DIR = path.join(os.tmpdir(), 'fs-extra', 'walk')
+    fse.emptyDir(TEST_DIR, function (err) {
+      if (err) return done(err)
+      fixtures.forEach(function (f) {
+        f = path.join(TEST_DIR, f)
+        fse.outputFileSync(f, path.basename(f, path.extname(f)))
+      })
+      done()
+    })
+  })
+
+  afterEach(function (done) {
+    fse.remove(TEST_DIR, done)
+  })
+
+  it('should work w/ streams 1', function (done) {
+    var items = []
+    fse.walk(TEST_DIR)
+      .on('data', function (item) {
+        assert.strictEqual(typeof item.stats, 'object') // verify that property is there
+        items.push(item.path)
+      })
+      .on('end', function () {
+        items.sort()
+        var expected = ['a', 'a/b', 'a/b/c', 'a/b/c/d.txt', 'a/e.jpg', 'h', 'h/i', 'h/i/j',
+          'h/i/j/k.txt', 'h/i/l.txt', 'h/i/m.jpg']
+        expected = expected.map(function (item) {
+          return path.join(path.join(TEST_DIR, item))
+        })
+        expected.unshift(TEST_DIR)
+
+        assert.deepEqual(items, expected)
+        done()
+      })
+  })
+
+  it('should work w/ streams 2/3', function (done) {
+    var items = []
+    fse.walk(TEST_DIR)
+      .on('readable', function () {
+        var item
+        while ((item = this.read())) {
+          assert.strictEqual(typeof item.stats, 'object') // verify that property is there
+          items.push(item.path)
+        }
+      })
+      .on('end', function () {
+        items.sort()
+        var expected = ['a', 'a/b', 'a/b/c', 'a/b/c/d.txt', 'a/e.jpg', 'h', 'h/i', 'h/i/j',
+          'h/i/j/k.txt', 'h/i/l.txt', 'h/i/m.jpg']
+        expected = expected.map(function (item) {
+          return path.join(path.join(TEST_DIR, item))
+        })
+        expected.unshift(TEST_DIR)
+
+        assert.deepEqual(items, expected)
+        done()
+      })
+  })
+})
diff --git a/lib/walk/index.js b/lib/walk/index.js
new file mode 100644
index 0000000..8626d47
--- /dev/null
+++ b/lib/walk/index.js
@@ -0,0 +1,5 @@
+var klaw = require('klaw')
+
+module.exports = {
+  walk: klaw
+}
diff --git a/package.json b/package.json
index 1f2b3c5..c7e0ac7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "fs-extra",
-  "version": "0.16.5",
+  "version": "1.0.0",
   "description": "fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf.",
   "homepage": "https://github.com/jprichardson/node-fs-extra",
   "repository": {
@@ -31,29 +31,31 @@
     "move"
   ],
   "author": "JP Richardson <jprichardson at gmail.com>",
-  "licenses": [
-    {
-      "type": "MIT",
-      "url": "http://github.com/jprichardson/node-fs-extra/raw/master/LICENSE"
-    }
-  ],
+  "license": "MIT",
   "dependencies": {
-    "graceful-fs": "^3.0.5",
-    "jsonfile": "^2.0.0",
-    "rimraf": "^2.2.8"
+    "graceful-fs": "^4.1.2",
+    "jsonfile": "^2.1.0",
+    "klaw": "^1.0.0"
   },
   "devDependencies": {
     "coveralls": "^2.11.2",
-    "istanbul": "^0.3.5",
-    "mocha": "^2.1.0",
+    "istanbul": "^0.4.5",
+    "minimist": "^1.1.1",
+    "mocha": "^3.1.2",
+    "proxyquire": "^1.7.10",
     "read-dir-files": "^0.1.1",
+    "rimraf": "^2.2.8",
     "secure-random": "^1.1.1",
-    "testutil": "^0.7.0"
+    "semver": "^5.3.0",
+    "standard": "^8.5.0"
   },
   "main": "./lib/index",
   "scripts": {
-    "coverage": "istanbul cover ./node_modules/.bin/_mocha -- --reporter list test/*.js",
+    "coverage": "istanbul cover -i 'lib/**' -x '**/__tests__/**' test.js",
     "coveralls": "npm run coverage && coveralls < coverage/lcov.info",
-    "test": "mocha test"
+    "lint": "standard",
+    "test-find": "find ./lib/**/__tests__ -name *.test.js | xargs mocha",
+    "test": "npm run lint && npm run unit",
+    "unit": "node test.js"
   }
 }
diff --git a/test.js b/test.js
new file mode 100644
index 0000000..70c70b1
--- /dev/null
+++ b/test.js
@@ -0,0 +1,30 @@
+var os = require('os')
+var path = require('path')
+var Mocha = require('mocha')
+var assign = require('./lib/util/assign')
+var fs = require('./')
+
+var argv = require('minimist')(process.argv.slice(2))
+
+var mochaOpts = assign({
+  ui: 'bdd',
+  reporter: 'dot',
+  timeout: 30000
+}, argv)
+
+var mocha = new Mocha(mochaOpts)
+
+fs.walk('./lib').on('readable', function () {
+  var item
+  while ((item = this.read())) {
+    if (!item.stats.isFile()) return
+    if (item.path.lastIndexOf('.test.js') !== (item.path.length - '.test.js'.length)) return
+    mocha.addFile(item.path)
+  }
+}).on('end', function () {
+  mocha.run(function (failures) {
+    require('./').remove(path.join(os.tmpdir(), 'fs-extra'), function () {
+      process.exit(failures)
+    })
+  })
+})
diff --git a/test/copy-sync.test.js b/test/copy-sync.test.js
deleted file mode 100644
index c9f3e7f..0000000
--- a/test/copy-sync.test.js
+++ /dev/null
@@ -1,223 +0,0 @@
-var assert = require('assert')
-var crypto = require('crypto')
-var path = require('path')
-var fs = require('../lib')
-var testutil = require('testutil')
-
-var testlib = require('./lib/util')
-
-var SIZE = 16 * 64 * 1024 + 7
-var DIR = ''
-
-describe("+ copySync()", function () {
-  beforeEach(function() {
-    DIR = testutil.createTestDir('fs-extra')
-  })
-
-  afterEach(function(done) {
-    fs.remove(DIR, done)
-  })
-
-  describe("> when the source is a file", function () {
-    it("should copy the file synchronously", function () {
-      var fileSrc = path.join(DIR, "TEST_fs-extra_src")
-      var fileDest = path.join(DIR, "TEST_fs-extra_copy")
-      var fileSrc = testlib.createFileWithData(fileSrc, SIZE)
-      var srcMd5 = crypto.createHash('md5').update(fs.readFileSync(fileSrc)).digest("hex")
-      var destMd5 = ''
-
-      fs.copySync(fileSrc, fileDest)
-
-      destMd5 = crypto.createHash('md5').update(fs.readFileSync(fileDest)).digest("hex")
-      assert.strictEqual(srcMd5, destMd5)
-    })
-
-    it("should follow symlinks", function () {
-      var fileSrc = path.join(DIR, "TEST_fs-extra_src")
-      var fileDest = path.join(DIR, "TEST_fs-extra_copy")
-      var linkSrc = path.join(DIR, "TEST_fs-extra_copy_link")
-      var fileSrc = testlib.createFileWithData(fileSrc, SIZE)
-      var srcMd5 = crypto.createHash('md5').update(fs.readFileSync(fileSrc)).digest("hex")
-      var destMd5 = ''
-
-      fs.symlinkSync(fileSrc, linkSrc)
-      fs.copySync(linkSrc, fileDest)
-      destMd5 = crypto.createHash('md5').update(fs.readFileSync(fileDest)).digest("hex")
-      assert.strictEqual(srcMd5, destMd5)
-    })
-
-    it("should maintain file mode", function () {
-      var fileSrc = path.join(DIR, "TEST_fs-extra_src")
-      var fileDest = path.join(DIR, "TEST_fs-extra_copy")
-      var fileSrc = testlib.createFileWithData(fileSrc, SIZE)
-
-      fs.chmodSync(fileSrc, 0750)
-      fs.copySync(fileSrc, fileDest)
-
-      var statSrc = fs.statSync(fileSrc)
-      var statDest = fs.statSync(fileDest)
-      assert.strictEqual(statSrc.mode, statDest.mode)
-    })
-
-    it("should only copy files allowed by filter regex", function() {
-      var srcFile1 = testlib.createFileWithData(path.join(DIR, "1.html"), SIZE)
-      var srcFile2 = testlib.createFileWithData(path.join(DIR, "2.css"), SIZE)
-      var srcFile3 = testlib.createFileWithData(path.join(DIR, "3.jade"), SIZE)
-      var destFile1 = path.join(DIR, "dest1.html")
-      var destFile2 = path.join(DIR, "dest2.css")
-      var destFile3 = path.join(DIR, "dest3.jade")
-      var filter = /.html$|.css$/i
-
-      fs.copySync(srcFile1, destFile1, {filter: filter})
-      fs.copySync(srcFile2, destFile2, {filter: filter})
-      fs.copySync(srcFile3, destFile3, {filter: filter})
-
-      assert(fs.existsSync(destFile1))
-      assert(fs.existsSync(destFile2))
-      assert(!fs.existsSync(destFile3))
-    })
-
-    it("should only copy files allowed by filter fn", function() {
-      var srcFile1 = testlib.createFileWithData(path.join(DIR, "1.html"), SIZE)
-      var srcFile2 = testlib.createFileWithData(path.join(DIR, "2.css"), SIZE)
-      var srcFile3 = testlib.createFileWithData(path.join(DIR, "3.jade"), SIZE)
-      var destFile1 = path.join(DIR, "dest1.html")
-      var destFile2 = path.join(DIR, "dest2.css")
-      var destFile3 = path.join(DIR, "dest3.jade")
-
-      var filter = function(s) { return s.split(".").pop() !== "css";}
-
-      fs.copySync(srcFile1, destFile1, filter)
-      fs.copySync(srcFile2, destFile2, filter)
-      fs.copySync(srcFile3, destFile3, filter)
-
-      assert(fs.existsSync(destFile1))
-      assert(!fs.existsSync(destFile2))
-      assert(fs.existsSync(destFile3))
-    })
-
-    describe("> when the destination dir does not exist", function () {
-      it('should create the destination directory and copy the file', function () {
-        var src = path.join(DIR, 'file.txt')
-        var dest = path.join(DIR, 'this/path/does/not/exist/copied.txt')
-        var data = "did it copy?\n"
-
-        fs.writeFileSync(src, data, 'utf8')
-        fs.copySync(src, dest)
-
-        var data2 = fs.readFileSync(dest, 'utf8')
-
-        assert.strictEqual(data, data2)
-      })
-    })
-  })
-
-  describe("> when the source is a directory", function() {
-    it("should copy the directory synchronously", function() {
-      var FILES = 2
-      var src = path.join(DIR, 'src')
-      var dest = path.join(DIR, 'dest')
-
-      var i, j
-
-      fs.mkdirsSync(src)
-
-      for (i = 0; i < FILES; ++i)
-        testlib.createFileWithData(path.join(src, i.toString()), SIZE)
-
-      var subdir = path.join(src, 'subdir')
-
-      fs.mkdirsSync(subdir)
-
-      for (i = 0; i < FILES; ++i)
-        testlib.createFileWithData(path.join(subdir, i.toString()), SIZE)
-
-      fs.copySync(src, dest)
-      assert(fs.existsSync(dest))
-
-      for (i = 0; i < FILES; ++i) 
-        assert(fs.existsSync(path.join(dest, i.toString())))
-
-      var destSub = path.join(dest, 'subdir')
-      for (j = 0; j < FILES; ++j) 
-        assert(fs.existsSync(path.join(destSub, j.toString())))
-    })
-
-    it("should preserve symbolic links", function() {
-      var src = path.join(DIR, 'src')
-      var dest = path.join(DIR, 'dest')
-
-      fs.mkdirsSync(src)
-      fs.symlinkSync('destination', path.join(src, 'symlink'))
-
-      fs.copySync(src, dest)
-
-      var link = fs.readlinkSync(path.join(dest, 'symlink'))
-      assert.strictEqual(link, 'destination')
-    })
-
-    it("should should apply filter recursively", function() {
-      var FILES = 2
-      var src = path.join(DIR, 'src')
-      var dest = path.join(DIR, 'dest')
-      var filter = /0$/i
-
-      fs.mkdirsSync(src)
-
-      for (var i = 0; i < FILES; ++i)
-        testlib.createFileWithData(path.join(src, i.toString()), SIZE)
-
-      var subdir = path.join(src, 'subdir')
-      fs.mkdirsSync(subdir)
-
-      for (i = 0; i < FILES; ++i)
-        testlib.createFileWithData(path.join(subdir, i.toString()), SIZE)
-
-      fs.copySync(src, dest, filter)
-
-      assert(fs.existsSync(dest))
-      assert(FILES>1)
-
-      for (i = 0; i < FILES; ++i) {
-        if (i==0) {
-          assert(fs.existsSync(path.join(dest, i.toString())))
-        } else {
-          assert(!fs.existsSync(path.join(dest, i.toString())))
-        }
-      }
-
-      var destSub = path.join(dest, 'subdir')
-      
-      for (var j = 0; j < FILES; ++j) {
-        if (j==0) {
-          assert(fs.existsSync(path.join(destSub, j.toString())))
-        } else {
-          assert(!fs.existsSync(path.join(destSub, j.toString())))
-        }
-      }
-    })
-
-    describe("> when the destination dir does not exist", function() {
-      it("should create the destination directory and copy the file", function() {
-        var src = path.join(DIR, 'data/')
-        fs.mkdirSync(src)
-
-        var d1 = "file1"
-        var d2 = "file2"
-        var f1 = fs.writeFileSync(path.join(src, "f1.txt"), d1)
-        var f2 = fs.writeFileSync(path.join(src, "f2.txt"), d2)
-
-        var dest = path.join(DIR, 'this/path/does/not/exist/outputDir')
-
-        fs.copySync(src, dest)
-
-        var o1 = fs.readFileSync(path.join(dest, 'f1.txt'), 'utf8')
-        var o2 = fs.readFileSync(path.join(dest, 'f2.txt'), 'utf8')
-
-        assert.strictEqual(d1, o1)
-        assert.strictEqual(d2, o2)
-      })
-    })
-  })
-})
-
diff --git a/test/copy.test.js b/test/copy.test.js
deleted file mode 100644
index 842c0f8..0000000
--- a/test/copy.test.js
+++ /dev/null
@@ -1,238 +0,0 @@
-var assert = require('assert')
-var crypto = require('crypto')
-var fs = require('../lib')
-var path = require('path')
-var testutil = require('testutil')
-var mkdirp = fs.mkdirs
-//var userid = require('userid')
-
-var testlib = require('./lib/util')
-
-var SIZE = 16 * 64 * 1024 + 7
-var DIR = ''
-
-describe('fs-extra', function() {
-  beforeEach(function() {
-    DIR = testutil.createTestDir('fs-extra')
-  })
-
-  afterEach(function() {
-    fs.removeSync(DIR)
-  })
-
-  describe('+ copy()', function() {
-    describe('> when the source is a file', function() {
-      it('should copy the file asynchronously', function(done) {
-        var fileSrc = path.join(DIR, "TEST_fs-extra_src")
-        var fileDest = path.join(DIR, "TEST_fs-extra_copy")
-        var fileSrc = testlib.createFileWithData(fileSrc, SIZE)
-        var srcMd5 = crypto.createHash('md5').update(fs.readFileSync(fileSrc)).digest("hex")
-        var destMd5 = ''
-
-        fs.copy(fileSrc, fileDest, function(err) {
-          destMd5 = crypto.createHash('md5').update(fs.readFileSync(fileDest)).digest("hex")
-          assert.strictEqual(srcMd5, destMd5)
-          done()
-        })
-      })
-      
-      it('should return an error if the source file does not exist', function(done) {
-        var fileSrc = "we-simply-assume-this-file-does-not-exist.bin"
-        var fileDest = path.join(DIR, "TEST_fs-extra_copy")
-        var destMd5 = ''
-
-        fs.copy(fileSrc, fileDest, function(err) {
-          assert(err)
-          done()
-        })
-      })
-
-      it("should only copy files allowed by filter regex", function(done) {
-        var srcFile1 = testlib.createFileWithData(path.join(DIR, "1.jade"), SIZE)
-        var destFile1 = path.join(DIR, "dest1.jade")
-        var filter = /.html$|.css$/i
-        fs.copy(srcFile1, destFile1, filter, function() {
-          assert(!fs.existsSync(destFile1))
-          done()
-        })
-      })
-
-      it("should only copy files allowed by filter fn", function(done) {
-        var srcFile1 = testlib.createFileWithData(path.join(DIR, "1.css"), SIZE)
-        var destFile1 = path.join(DIR, "dest1.css")
-        var filter = function(s) { return s.split(".").pop() !== "css";}
-        fs.copy(srcFile1, destFile1, filter, function() {
-          assert(!fs.existsSync(destFile1))
-          done()
-        })
-      })
-
-      it("accepts options object in place of filter", function(done) {
-        var srcFile1 = testlib.createFileWithData(path.join(DIR, "1.jade"), SIZE)
-        var destFile1 = path.join(DIR, "dest1.jade")
-        var options = {filter: /.html$|.css$/i}
-        fs.copy(srcFile1, destFile1, options, function() {
-          assert(!fs.existsSync(destFile1))
-          done()
-        })
-      })
-
-      describe('> when the destination dir does not exist', function() {
-        it('should create the destination directory and copy the file', function(done) {
-          var src = path.join(DIR, 'file.txt')
-          var dest = path.join(DIR, 'this/path/does/not/exist/copied.txt')
-          var data = "did it copy?\n"
-
-          fs.writeFileSync(src, data, 'utf8')
-
-          fs.copy(src, dest, function(err) {
-            var data2 = fs.readFileSync(dest, 'utf8')
-            assert.strictEqual(data, data2)
-            done(err)
-          })
-        })
-      })
-    })
-
-    describe('> when the source is a directory', function() {
-      describe('> when the source directory does not exist', function() {
-        it('should return an error', function(done) {
-          var ts = path.join(DIR, 'this_dir_does_not_exist')
-          var td = path.join(DIR, 'this_dir_really_does_not_matter')
-          fs.copy(ts, td, function(err) {
-            assert(err)
-            done()
-          })
-        })
-      })
-
-      it('should copy the directory asynchronously', function(done) {
-        var FILES = 2
-        var src = path.join(DIR, 'src')
-        var dest = path.join(DIR, 'dest')
-
-        mkdirp(src, function(err) {
-          for (var i = 0; i < FILES; ++i)
-            testlib.createFileWithData(path.join(src, i.toString()), SIZE)
-
-          var subdir = path.join(src, 'subdir')
-          mkdirp(subdir, function(err) {
-            for (var i = 0; i < FILES; ++i)
-              testlib.createFileWithData(path.join(subdir, i.toString()), SIZE)
-
-            fs.copy(src, dest, function(err) {
-              assert.ifError(err)
-              assert(fs.existsSync(dest))
-
-              for (var i = 0; i < FILES; ++i)
-                assert(fs.existsSync(path.join(dest, i.toString())))
-
-
-              var destSub = path.join(dest, 'subdir')
-              for (var j = 0; j < FILES; ++j)
-                assert(fs.existsSync(path.join(destSub, j.toString())))
-
-              done()
-            })
-          })
-        })
-      })
-
-      describe('> when the destination dir does not exist', function() {
-        it('should create the destination directory and copy the file', function(done) {
-          var src = path.join(DIR, 'data/')
-          fs.mkdirsSync(src)
-          var d1 = "file1"
-          var d2 = "file2"
-
-          var f1 = fs.writeFileSync(path.join(src, "f1.txt"), d1)
-          var f2 = fs.writeFileSync(path.join(src, "f2.txt"), d2)
-
-          var dest = path.join(DIR, 'this/path/does/not/exist/outputDir')
-
-          fs.copy(src, dest, function(err) {
-            var o1 = fs.readFileSync(path.join(dest, 'f1.txt'), 'utf8')
-            var o2 = fs.readFileSync(path.join(dest, 'f2.txt'), 'utf8')
-
-            assert.strictEqual(d1, o1)
-            assert.strictEqual(d2, o2)
-
-            done(err)
-          })
-        })
-      })
-
-      describe('> when src dir does not exist', function() {
-        it('should return an error', function(done) {
-          fs.copy('/does/not/exist', '/something/else', function(err) {
-            assert(err instanceof Error)
-            done()
-          })
-        })
-      })
-    })
-
-    describe.skip('> REGRESSIONS', function() {
-      //pretty UNIX specific, may not pass on windows... only test on Mac OS X 10.9
-      it('should maintain file permissions and ownership', function(done) {
-
-        //http://man7.org/linux/man-pages/man2/stat.2.html
-        var S_IFREG = 0100000 //regular file
-        var S_IFDIR = 0040000 //directory
-
-        var permDir = path.join(DIR, 'perms')
-        fs.mkdirSync(permDir)
-
-        var srcDir = path.join(permDir, 'src')
-        fs.mkdirSync(srcDir)
-
-        var f1 = path.join(srcDir, 'f1.txt')
-        fs.writeFileSync(f1, '')
-        fs.chmodSync(f1, 0666)
-        fs.chownSync(f1, process.getuid(), userid.gid('wheel'))
-        var f1stats = fs.lstatSync(f1)
-        assert.strictEqual(f1stats.mode - S_IFREG, 0666)
-
-        var d1 = path.join(srcDir, 'somedir')
-        fs.mkdirSync(d1)
-        fs.chmodSync(d1, 0777)
-        fs.chownSync(d1, process.getuid(), userid.gid('staff'))
-        var d1stats = fs.lstatSync(d1)
-        assert.strictEqual(d1stats.mode - S_IFDIR, 0777)
-
-        var f2 = path.join(d1, 'f2.bin')
-        fs.writeFileSync(f2, '')
-        fs.chmodSync(f2, 0777)
-        fs.chownSync(f2, process.getuid(), userid.gid('staff'))
-        var f2stats = fs.lstatSync(f2)
-        assert.strictEqual(f2stats.mode - S_IFREG, 0777)
-
-        var d2 = path.join(srcDir, 'crazydir')
-        fs.mkdirSync(d2)
-        fs.chmodSync(d2, 0444)
-        fs.chownSync(d2, process.getuid(), userid.gid('wheel'))
-        var d2stats = fs.lstatSync(d2)
-        assert.strictEqual(d2stats.mode - S_IFDIR, 0444)
-
-        var destDir = path.join(permDir, 'dest')
-        fs.copy(srcDir, destDir, function(err) {
-          assert.ifError(err)
-
-          var newf1stats = fs.lstatSync(path.join(permDir, 'dest/f1.txt'))
-          var newd1stats = fs.lstatSync(path.join(permDir, 'dest/somedir'))
-          var newf2stats = fs.lstatSync(path.join(permDir, 'dest/somedir/f2.bin'))
-          var newd2stats = fs.lstatSync(path.join(permDir, 'dest/crazydir'))
-
-          assert.strictEqual(newf1stats.mode, f1stats.mode)
-          assert.strictEqual(newd1stats.mode, d1stats.mode)
-          assert.strictEqual(newf2stats.mode, f2stats.mode)
-          assert.strictEqual(newd2stats.mode, d2stats.mode)
-
-          done();  
-        })
-      })
-    })
-  })
-})
-
-
diff --git a/test/fs-integration.test.js b/test/fs-integration.test.js
deleted file mode 100644
index 0bf98a7..0000000
--- a/test/fs-integration.test.js
+++ /dev/null
@@ -1,23 +0,0 @@
-var assert = require('assert')
-var path = require('path')
-var fse = require('../')
-var testutil = require('testutil')
-
-var TEST_DIR;
-
-describe('native fs', function() {
-  beforeEach(function() {
-    TEST_DIR = testutil.createTestDir('fs-extra')
-  })
-
-  afterEach(function(done) {
-    fse.remove(TEST_DIR, done)
-  })
-
-  it('should use native fs methods', function() {
-    var file = path.join(TEST_DIR, 'write.txt')
-    fse.writeFileSync(file, 'hello')
-    var data = fse.readFileSync(file, 'utf8')
-    assert.equal(data, 'hello')
-  })
-})
\ No newline at end of file
diff --git a/test/json.test.js b/test/json.test.js
deleted file mode 100644
index 1240fa8..0000000
--- a/test/json.test.js
+++ /dev/null
@@ -1,50 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var testutil = require('testutil')
-var fse = require('../')
-
-var TEST_DIR = null
-
-describe('json', function() {
-  beforeEach(function() {
-    TEST_DIR = testutil.createTestDir('fs-extra')    
-  })
-
-  afterEach(function(done) {
-    fse.remove(TEST_DIR, done)
-  })
-
-  describe('+ outputJsonSync(file, data)', function() {
-    it('should write the file regardless of whether the directory exists or not', function() {
-      var file = path.join(TEST_DIR, 'this-dir', 'does-not', 'exist', 'file.json')
-      assert(!fs.existsSync(file))
-
-      var data = {name: 'JP'}
-      fse.outputJsonSync(file, data)
-      
-      assert(fs.existsSync(file))
-      var newData = JSON.parse(fs.readFileSync(file, 'utf8'))
-
-      assert.equal(data.name, newData.name)
-    })
-  })
-
-  describe('+ outputJson(file, data)', function() {
-    it('should write the file regardless of whether the directory exists or not', function(done) {
-      var file = path.join(TEST_DIR, 'this-dir', 'prob-does-not', 'exist', 'file.json')
-      assert(!fs.existsSync(file))
-
-      var data = {name: 'JP'}
-      fse.outputJson(file, data, function(err) {
-        if (err) return done(err)
-
-        assert(fs.existsSync(file))
-        var newData = JSON.parse(fs.readFileSync(file, 'utf8'))
-
-        assert.equal(data.name, newData.name)
-        done()
-      })
-    })
-  })
-})
diff --git a/test/lib/util.js b/test/lib/util.js
deleted file mode 100644
index b4658ed..0000000
--- a/test/lib/util.js
+++ /dev/null
@@ -1,36 +0,0 @@
-var fs = require('fs')
-var path = require('path')
-var os = require('os')
-var rimraf = require('rimraf')
-var sr = require('secure-random')
-
-function tmpdir() {
-  if (os.type().toLowerCase().indexOf('win') === 0)
-    return process.env['TEMP']
-  else
-    return '/tmp'
-}
-
-function createFileWithData(file, size) {
-  fs.writeFileSync(file, sr.randomBuffer(size))
-  return file
-}
-
-function createTestDir() {
-  var app = 'fs-extra'
-  var dir = path.join(tmpdir(), 'test-' + app)
-  if (fs.existsSync(dir)) {
-    var files = fs.readdirSync(dir)
-    files.forEach(function(file) {
-      return rimraf.sync(path.join(dir, file))
-    })
-  } else {
-    fs.mkdirSync(dir)
-  }
-  return dir
-}
-
-module.exports = {
-  createFileWithData: createFileWithData,
-  createTestDir: createTestDir
-}
diff --git a/test/mkdirp/README.md b/test/mkdirp/README.md
deleted file mode 100644
index a211103..0000000
--- a/test/mkdirp/README.md
+++ /dev/null
@@ -1 +0,0 @@
-These tests imported from https://github.com/substack/node-mkdirp.
\ No newline at end of file
diff --git a/test/mkdirp/chmod.test.js b/test/mkdirp/chmod.test.js
deleted file mode 100644
index b40a975..0000000
--- a/test/mkdirp/chmod.test.js
+++ /dev/null
@@ -1,52 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var fse = require('../../')
-var testutil = require('testutil')
-
-var TEST_DIR = ''
-
-var ps = []
-for (var i = 0; i < 25; i++) {
-  var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-  ps.push(dir)
-}
-
-var file = ps.join('/')
-
-describe('mkdirp / chmod', function() {
-  beforeEach(function() {
-    TEST_DIR = testutil.createTestDir('fs-extra')
-    file = path.join(TEST_DIR, file)
-  })
-
-  afterEach(function(done) {
-    fse.remove(TEST_DIR, done)
-  })
-
-  it('chmod-pre', function (done) {
-    var mode = 0744
-    fse.mkdirp(file, mode, function (er) {
-      assert.ifError(er, 'should not error')
-      fs.stat(file, function (er, stat) {
-        assert.ifError(er, 'should exist')
-        assert.ok(stat && stat.isDirectory(), 'should be directory')
-        assert.equal(stat && stat.mode & 0777, mode, 'should be 0744')
-        done()
-      })
-    })
-  })
-
-  it('chmod', function (done) {
-    var mode = 0755
-    fse.mkdirp(file, mode, function (er) {
-      assert.ifError(er, 'should not error')
-      fs.stat(file, function (er, stat) {
-        assert.ifError(er, 'should exist')
-        assert.ok(stat && stat.isDirectory(), 'should be directory')
-        done()
-      })
-    })
-  })
-})
-
diff --git a/test/mkdirp/clobber.test.js b/test/mkdirp/clobber.test.js
deleted file mode 100644
index 9f53f33..0000000
--- a/test/mkdirp/clobber.test.js
+++ /dev/null
@@ -1,41 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var fse = require('../../')
-var testutil = require('testutil')
-
-describe('mkdirp / clobber', function() {
-  var ps, file, itw
-
-  before(function(done) {
-    ps = [ '', testutil.createTestDir('fs-extra')]
-
-    for (var i = 0; i < 25; i++) {
-      var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-      ps.push(dir)
-    }
-
-    file = ps.join('/')
-
-    // a file in the way
-    itw = ps.slice(0, 3).join('/')
-
-    console.error("about to write to "+itw)
-    fs.writeFileSync(itw, 'I AM IN THE WAY, THE TRUTH, AND THE LIGHT.')
-
-    fs.stat(itw, function (er, stat) {
-      assert.ifError(er)
-      assert.ok(stat && stat.isFile(), 'should be file')
-      done()
-    })
-  })
-
-  it('should clobber', function (done) {
-    fse.mkdirp(file, 0755, function (err) {
-      assert.ok(err)
-      assert.equal(err.code, 'ENOTDIR')
-      done()
-    })
-  })
-})
-
diff --git a/test/mkdirp/mkdirp.test.js b/test/mkdirp/mkdirp.test.js
deleted file mode 100644
index b82b46d..0000000
--- a/test/mkdirp/mkdirp.test.js
+++ /dev/null
@@ -1,34 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var fse = require('../../')
-var testutil = require('testutil')
-
-describe('mkdirp / mkdirp', function() {
-  var TEST_DIR
-
-  before(function() {
-    TEST_DIR = testutil.createTestDir('fs-extra')
-  })
-
-  it('woo', function (done) {
-    var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    
-    var file = TEST_DIR + [x,y,z].join('/')
-    
-    fse.mkdirp(file, 0755, function (err) {
-      assert.ifError(err)
-      fs.exists(file, function (ex) {
-        assert.ok(ex, 'file created')
-        fs.stat(file, function (err, stat) {
-          assert.ifError(err)
-          assert.equal(stat.mode & 0777, 0755)
-          assert.ok(stat.isDirectory(), 'target not a directory')
-          done()
-        })
-      })
-    })
-  })
-})
\ No newline at end of file
diff --git a/test/mkdirp/opts_fs.test.js b/test/mkdirp/opts_fs.test.js
deleted file mode 100644
index d153978..0000000
--- a/test/mkdirp/opts_fs.test.js
+++ /dev/null
@@ -1,32 +0,0 @@
-return // skip for now
-
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var fse = require('../../')
-var testutil = require('testutil')
-var mockfs = require('mock-fs')
-
-describe('mkdirp / opts_fs', function() {
-  it('opts.fs', function (done) {    
-    var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    
-    var file = '/beep/boop/' + [x,y,z].join('/')
-    var xfs = mockfs.fs()
-    
-    fse.mkdirp(file, { fs: xfs, mode: 0755 }, function (err) {
-      assert.ifError(err)
-      xfs.exists(file, function (ex) {
-        assert.ok(ex, 'created file')
-        xfs.stat(file, function (err, stat) {
-          assert.ifError(err)
-          assert.equal(stat.mode & 0777, 0755)
-          assert.ok(stat.isDirectory(), 'target not a directory')
-          done()
-        })
-      })  
-    })
-  })
-})
diff --git a/test/mkdirp/opts_fs_sync.test.js b/test/mkdirp/opts_fs_sync.test.js
deleted file mode 100644
index 90d925e..0000000
--- a/test/mkdirp/opts_fs_sync.test.js
+++ /dev/null
@@ -1,30 +0,0 @@
-return // skip for now
-
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var fse = require('../../')
-var testutil = require('testutil')
-var mockfs = require('mock-fs')
-
-describe('mkdirp / opts_fs', function() {
-  it('opts.fs', function (done) {    
-    var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    
-    var file = '/beep/boop/' + [x,y,z].join('/')
-    var xfs = mockfs.fs()
-    
-    fse.mkdirpSync(file, { fs: xfs, mode: 0755 })
-    xfs.exists(file, function (ex) {
-      assert.ok(ex, 'created file')
-      xfs.stat(file, function (err, stat) {
-        assert.ifError(err)
-        assert.equal(stat.mode & 0777, 0755)
-        assert.ok(stat.isDirectory(), 'target not a directory')
-        done()
-      })
-    })  
-  })
-})
diff --git a/test/mkdirp/perm.test.js b/test/mkdirp/perm.test.js
deleted file mode 100644
index eb93566..0000000
--- a/test/mkdirp/perm.test.js
+++ /dev/null
@@ -1,41 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var fse = require('../../')
-var testutil = require('testutil')
-
-describe('mkdirp / perm', function() {
-  var TEST_DIR
-
-  before(function() {
-    TEST_DIR = testutil.createTestDir('fs-extra')
-  })
-
-  afterEach(function(done) {
-    fse.remove(TEST_DIR, done)
-  })
-
-  it('async perm', function (done) {
-    var file = path.join(TEST_DIR, (Math.random() * (1<<30)).toString(16))
-    
-    fse.mkdirp(file, 0755, function (err) {
-      assert.ifError(err)
-      fs.exists(file, function (ex) {
-        assert.ok(ex, 'file created')
-        fs.stat(file, function (err, stat) {
-          assert.ifError(err)
-          assert.equal(stat.mode & 0777, 0755)
-          assert.ok(stat.isDirectory(), 'target not a directory')
-          done()
-        })
-      })
-    })
-  })
-
-  it('async root perm', function (done) {
-    fse.mkdirp('/tmp', 0755, function (err) {
-      assert.ifError(err)
-      done()
-    })
-  })
-})
diff --git a/test/mkdirp/perm_sync.js b/test/mkdirp/perm_sync.js
deleted file mode 100644
index d7245d1..0000000
--- a/test/mkdirp/perm_sync.js
+++ /dev/null
@@ -1,46 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var fse = require('../../')
-var testutil = require('testutil')
-
-describe('mkdirp / perm_sync', function() {
-  var TEST_DIR
-
-  before(function() {
-    TEST_DIR = testutil.createTestDir('fs-extra')
-  })
-
-  afterEach(function(done) {
-    fse.remove(TEST_DIR, done)
-  })
-
-  it('sync perm', function (done) {
-    var file = path.join(TEST_DIR, (Math.random() * (1<<30)).toString(16) + '.json')
-    
-    fse.mkdirpSync(file, 0755)
-    fs.exists(file, function (ex) {
-      assert.ok(ex, 'file created')
-      fs.stat(file, function (err, stat) {
-        assert.ifError(err)
-        assert.equal(stat.mode & 0777, 0755)
-        assert.ok(stat.isDirectory(), 'target not a directory')
-        done()
-      })
-    })
-  })
-
-  it('sync root perm', function (done) {    
-    var file = TEST_DIR
-    fse.mkdirpSync(file, 0755)
-    fs.exists(file, function (ex) {
-      assert.ok(ex, 'file created')
-      fs.stat(file, function (err, stat) {
-        assert.ifError(err)
-        assert.ok(stat.isDirectory(), 'target not a directory')
-        done()
-      })
-    })
-  })
-})
-
diff --git a/test/mkdirp/race.test.js b/test/mkdirp/race.test.js
deleted file mode 100644
index 3dd6ed1..0000000
--- a/test/mkdirp/race.test.js
+++ /dev/null
@@ -1,42 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var fse = require('../../')
-var testutil = require('testutil')
-
-describe('mkdirp / race', function() {
-  it('race', function (done) {
-
-    var ps = [ '', testutil.createTestDir('fs-extra') ]
-    
-    for (var i = 0; i < 25; i++) {
-      var dir = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-      ps.push(dir)
-    }
-    var file = ps.join('/')
-    
-    var res = 2
-    mk(file, function () {
-      if (--res === 0) done()
-    })
-    
-    mk(file, function () {
-      if (--res === 0) done()
-    })
-    
-    function mk (file, cb) {
-      fse.mkdirp(file, 0755, function (err) {
-        assert.ifError(err)
-        fs.exists(file, function (ex) {
-          assert.ok(ex, 'file created')
-          fs.stat(file, function (err, stat) {
-            assert.ifError(err)
-            assert.equal(stat.mode & 0777, 0755)
-            assert.ok(stat.isDirectory(), 'target not a directory')
-            if (cb) cb()
-          })
-        })
-      })
-    }
-  })
-})
diff --git a/test/mkdirp/rel.test.js b/test/mkdirp/rel.test.js
deleted file mode 100644
index 7d26904..0000000
--- a/test/mkdirp/rel.test.js
+++ /dev/null
@@ -1,32 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var fse = require('../../')
-var testutil = require('testutil')
-
-describe('mkdirp / rel', function() {
-  it('rel', function (done) {
-    var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    
-    var cwd = process.cwd()
-    process.chdir(testutil.createTestDir('fs-extra'))
-    
-    var file = [x,y,z].join('/')
-    
-    fse.mkdirp(file, 0755, function (err) {
-      assert.ifError(err)
-      fs.exists(file, function (ex) {
-        assert.ok(ex, 'file created')
-        fs.stat(file, function (err, stat) {
-          assert.ifError(err)
-          process.chdir(cwd)
-          assert.equal(stat.mode & 0777, 0755)
-          assert.ok(stat.isDirectory(), 'target not a directory')
-          done()
-        })
-      })
-    })
-  })
-})
diff --git a/test/mkdirp/return.test.js b/test/mkdirp/return.test.js
deleted file mode 100644
index 1d93be8..0000000
--- a/test/mkdirp/return.test.js
+++ /dev/null
@@ -1,29 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var fse = require('../../')
-var testutil = require('testutil')
-
-describe('mkdirp / return value', function() {
-  it('should', function (done) {
-    var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-
-    var dir =  testutil.createTestDir('fs-extra') + '/'
-    var file = dir + [x,y,z].join('/')
-
-    // should return the first dir created.
-    // By this point, it would be profoundly surprising if /tmp didn't
-    // already exist, since every other test makes things in there.
-    fse.mkdirp(file, function (err, made) {
-      assert.ifError(err)
-      assert.equal(made, dir + x)
-      fse.mkdirp(file, function (err, made) {
-        assert.ifError(err)
-        assert.equal(made, null)
-        done()
-      })
-    })
-  })
-})
diff --git a/test/mkdirp/return_sync.test.js b/test/mkdirp/return_sync.test.js
deleted file mode 100644
index d2324d8..0000000
--- a/test/mkdirp/return_sync.test.js
+++ /dev/null
@@ -1,27 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var fse = require('../../')
-var testutil = require('testutil')
-
-describe('mkdirp / return value', function() {
-  it('should', function () {
-    var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-
-    var dir =  testutil.createTestDir('fs-extra') + '/'
-    var file = dir + [x,y,z].join('/')
-
-    // should return the first dir created.
-    // By this point, it would be profoundly surprising if /tmp didn't
-    // already exist, since every other test makes things in there.
-    // Note that this will throw on failure, which will fail the test.
-    var made = fse.mkdirpSync(file)
-    assert.equal(made, dir + x)
-
-    // making the same file again should have no effect.
-    made = fse.mkdirpSync(file)
-    assert.equal(made, null)
-  })
-})
diff --git a/test/mkdirp/root.test.js b/test/mkdirp/root.test.js
deleted file mode 100644
index 4743fbf..0000000
--- a/test/mkdirp/root.test.js
+++ /dev/null
@@ -1,22 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var fse = require('../../')
-var testutil = require('testutil')
-
-describe('mkdirp / root', function () {
-
-  it('should', function(done) {
-    // '/' on unix, 'c:/' on windows.
-    var file = path.resolve('/')
-
-    fse.mkdirp(file, 0755, function (err) {
-      if (err) throw err
-      fs.stat(file, function (er, stat) {
-        if (er) throw er
-        assert.ok(stat.isDirectory(), 'target is a directory')
-        done()
-      })
-    })
-  })
-})
\ No newline at end of file
diff --git a/test/mkdirp/sync.test.js b/test/mkdirp/sync.test.js
deleted file mode 100644
index 883db23..0000000
--- a/test/mkdirp/sync.test.js
+++ /dev/null
@@ -1,31 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var fse = require('../../')
-var testutil = require('testutil')
-
-describe('mkdirp / sync', function() {
-  it('should', function (done) {
-    var x = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var y = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-    var z = Math.floor(Math.random() * Math.pow(16,4)).toString(16)
-
-    var file = testutil.createTestDir('fs-extra') + '/' + [x,y,z].join('/')
-
-    try {
-      fse.mkdirpSync(file, 0755)
-    } catch (err) {
-      assert.fail(err)
-    }
-
-    fs.exists(file, function (ex) {
-      assert.ok(ex, 'file created')
-      fs.stat(file, function (err, stat) {
-        assert.ifError(err)
-        assert.equal(stat.mode & 0777, 0755)
-        assert.ok(stat.isDirectory(), 'target not a directory')
-        done()
-      })
-    })
-  })
-})
diff --git a/test/mkdirp/umask.test.js b/test/mkdirp/umask.test.js
deleted file mode 100644
index 3b203ec..0000000
--- a/test/mkdirp/umask.test.js
+++ /dev/null
@@ -1,75 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var testutil = require('../lib/util')
-var fse = require('../../')
-
-'use strict'
-
-var TEST_DIR = ''
-var oct777 = parseInt('777', 8)
-
-describe('mkdirp', function() {
-  var _rndDir
-
-  beforeEach(function() {
-    TEST_DIR = testutil.createTestDir()
-    TEST_DIR = path.join(TEST_DIR, 'mkdirp')
-
-    // verify clean directory
-    assert(!fs.existsSync(TEST_DIR))
-    fs.mkdirSync(TEST_DIR)
-
-    // for actual tests
-    var x = Math.floor(Math.random() * Math.pow(16,6)).toString(16)
-    var y = Math.floor(Math.random() * Math.pow(16,6)).toString(16)
-    var z = Math.floor(Math.random() * Math.pow(16,6)).toString(16)
-    _rndDir = path.join(TEST_DIR, [x, y, z].join(path.sep))
-
-    // just to be safe, although unnecessary
-    assert(!fs.existsSync(_rndDir))
-  })
-
-  afterEach(function() {
-    fse.removeSync(TEST_DIR)
-  })
-
-  describe('umask', function() {
-    describe('async', function() {
-      it('should have proper umask', function(done) {
-        fse.mkdirp(_rndDir, function (err) {
-          assert.ifError(err)
-          fs.exists(_rndDir, function (ex) {
-            assert.ok(ex, 'file created')
-            fs.stat(_rndDir, function (err, stat) {
-              assert.ifError(err)
-              assert.equal(stat.mode & oct777, oct777 & (~process.umask()))
-              assert.ok(stat.isDirectory(), 'target not a directory')
-              done()
-            })
-          })
-        })
-      })
-    })
-
-    describe('sync', function() {
-      it('should have proper umask', function(done) {
-        try {
-          fse.mkdirpSync(_rndDir)
-        } catch (err) {
-          return done(err)
-        }
-
-        fs.exists(_rndDir, function (ex) {
-          assert.ok(ex, 'file created')
-          fs.stat(_rndDir, function (err, stat) {
-            assert.ifError(err)
-            assert.equal(stat.mode & oct777, (oct777 & (~process.umask())))
-            assert.ok(stat.isDirectory(), 'target not a directory')
-            done()
-          })
-        })
-      })
-    })
-  })
-})
diff --git a/test/mocha.opts b/test/mocha.opts
deleted file mode 100644
index 951f112..0000000
--- a/test/mocha.opts
+++ /dev/null
@@ -1,5 +0,0 @@
---reporter spec
---ui bdd
---growl
---timeout 7000
---recursive
\ No newline at end of file
diff --git a/test/move.test.js b/test/move.test.js
deleted file mode 100644
index 6418c79..0000000
--- a/test/move.test.js
+++ /dev/null
@@ -1,262 +0,0 @@
-var assert = require('assert')
-var path = require('path')
-var rimraf = require('rimraf')
-var fs =  require('fs')
-var fse = require('../')
-var testutil = require('./lib/util')
-
-var TEST_DIR = ''
-var FIXTURES_DIR = ''
-var SRC_FIXTURES_DIR = 'test/fixtures/move'
-
-// makes fs.rename return cross-device error.
-var mock_fs = {}
-mock_fs.rename = function(src, dest, callback) {
-  setTimeout(function() {
-    var err = new Error()
-    err.code = 'EXDEV'
-    callback(err)
-  }, 10)
-}
-
-describe("move", function() {
-  beforeEach(function() {
-    TEST_DIR = testutil.createTestDir('fs-extra')
-    TEST_DIR = path.join(TEST_DIR, 'move')
-    if (!fs.existsSync(TEST_DIR))
-      fs.mkdirSync(TEST_DIR)
-    FIXTURES_DIR = path.join(TEST_DIR, 'fixtures')
-    fse.copySync(SRC_FIXTURES_DIR, FIXTURES_DIR)
-  })
-
-  afterEach(function() {
-    rimraf.sync(TEST_DIR)
-  })
-
-  it("should rename a file on the same device", function (done) {
-    var src = FIXTURES_DIR + '/a-file'
-    var dest = FIXTURES_DIR + '/a-file-dest'
-
-    fse.move(src, dest, function (err) {
-      assert.ifError(err)
-      fs.readFile(dest, 'utf8', function (err, contents) {
-        assert.ifError(err)
-        assert.strictEqual(contents, "sonic the hedgehog\n")
-        done()
-      })
-    })
-  })
-
-  it("should not overwrite if clobber = false", function (done) {
-    var src = FIXTURES_DIR + "/a-file"
-    var dest = FIXTURES_DIR + "/a-folder/another-file"
-
-    // verify file exists already
-    assert(fs.existsSync(dest))
-
-    fse.move(src, dest, {clobber: false}, function (err) {
-      assert.ok(err && err.code === 'EEXIST', "throw EEXIST")
-      done()
-    })
-  })
-
-  it("should not create directory structure if mkdirp is false", function (done) {
-    var src = FIXTURES_DIR + "/a-file"
-    var dest = FIXTURES_DIR + "/does/not/exist/a-file-dest"
-
-    // verify dest directory does not exist
-    assert(!fs.existsSync(path.dirname(dest)))
-
-    fse.move(src, dest, {mkdirp: false}, function (err) {
-      assert.strictEqual(err.code, 'ENOENT')
-      done()
-    })
-  })
-
-  it("should create directory structure by default", function (done) {
-    var src = FIXTURES_DIR + "/a-file"
-    var dest = FIXTURES_DIR + "/does/not/exist/a-file-dest"
-
-    // verify dest directory does not exist
-    assert(!fs.existsSync(path.dirname(dest)))
-
-    fse.move(src, dest, function (err) {
-      assert.ifError(err)
-      fs.readFile(dest, 'utf8', function (err, contents) {
-        assert.ifError(err)
-        assert.strictEqual(contents, "sonic the hedgehog\n")
-        done()
-      })
-    })
-  })
-
-  it("should work across devices", function (done) {
-    var src = FIXTURES_DIR + "/a-file"
-    var dest = FIXTURES_DIR + "/a-file-dest"
-
-    var oldRename = fs.rename
-    fs.rename = mock_fs.rename
-
-    fse.move(src, dest, function (err) {
-      assert.ifError(err)
-      fs.readFile(dest, 'utf8', function (err, contents) {
-        assert.ifError(err)
-        assert.strictEqual(contents, "sonic the hedgehog\n")
-
-        // restore
-        fs.rename = oldRename
-
-        done()
-      })
-    })
-  })
-
-  it("should move folders", function (done) {
-    var src = FIXTURES_DIR + "/a-folder"
-    var dest = FIXTURES_DIR + "/a-folder-dest"
-
-    // verify it doesn't exist
-    assert(!fs.existsSync(dest))
-
-    fse.move(src, dest, function (err) {
-      assert.ifError(err)
-      fs.readFile(dest + "/another-file", 'utf8', function (err, contents) {
-        assert.ifError(err)
-        assert.strictEqual(contents, "tails\n")
-        done()
-      })
-    })
-  })
-
-  it("should move folders across devices", function (done) {
-    var src = FIXTURES_DIR + "/a-folder"
-    var dest = FIXTURES_DIR + "/a-folder-dest"
-
-    var oldRename = fs.rename
-    fs.rename = mock_fs.rename
-
-    fse.move(src, dest, function (err) {
-      assert.ifError(err)
-      fs.readFile(dest + "/another-folder/file3", 'utf8', function (err, contents) {
-        assert.ifError(err)
-        assert.strictEqual(contents, "knuckles\n")
-
-        // restore
-        fs.rename = oldRename
-
-        done()
-      })
-    })
-  })
-
-  describe.skip('> when trying to a move a folder into itself', function() {
-    it('should produce an error', function(done) {
-      var SRC_DIR = path.join(TEST_DIR, 'test')
-      var DEST_DIR = path.join(TEST_DIR, 'test', 'test')
-
-      assert(!fs.existsSync(SRC_DIR))
-      fs.mkdirSync(SRC_DIR)
-      assert(fs.existsSync(SRC_DIR))
-
-      fse.move(SRC_DIR, DEST_DIR, function(err) {
-        assert(fs.existsSync(SRC_DIR))
-        assert(err)
-        done()
-      })
-    })
-  })
-
-  // tested on Linux ubuntu 3.13.0-32-generic #57-Ubuntu SMP i686 i686 GNU/Linux
-  // this won't trigger a bug on Mac OS X Yosimite with a USB drive (/Volumes)
-  // see issue #108
-  describe('> when actually trying to a move a folder across devices', function() {
-    var differentDevice = '/mnt'
-    var __skipTests = false
-
-    // must set this up, if not, exit silently
-    if (!fs.existsSync(differentDevice)) {
-      console.log('Skipping cross-device move test')
-      __skipTests = true
-    }
-
-    // make sure we have permission on device
-    try {
-      fs.writeFileSync(path.join(differentDevice, 'file'), 'hi')
-    } catch (err) {
-      console.log("Can't write to device. Skipping test.")
-      __skipTests = true
-    }
-
-    var _it = __skipTests ? it.skip : it
-
-    describe('> just the folder', function() {
-      _it('should move the folder', function(done) {
-        var src = '/mnt/some/weird/dir-really-weird'
-        var dest = path.join(TEST_DIR, 'device-weird')
-
-        if (!fs.existsSync(src))
-          fse.mkdirpSync(src)
-
-        assert(!fs.existsSync(dest))
-
-        assert(fs.lstatSync(src).isDirectory())
-
-        fse.move(src, dest, function(err) {
-          assert.ifError(err)
-          assert(fs.existsSync(dest))
-          //console.log(path.normalize(dest))
-          assert(fs.lstatSync(dest).isDirectory())
-          done()
-        })
-      })
-    })
-
-    describe('> a folder with a bunch of stuff', function() {
-
-    })
-
-    describe('> when clobber = true', function() {
-      describe('> when dest is a directory', function() {
-        it('should clobber the destination', function(done) {
-          // use fixtures dir as dest since it has stuff
-          var dest = FIXTURES_DIR
-          var paths = fs.readdirSync(dest)
-
-          // verify dest has stuff
-          assert(paths.indexOf('a-file') >= 0)
-          assert(paths.indexOf('a-folder') >= 0)
-
-          // create new source dir
-          var src = path.join(TEST_DIR, 'src')
-          fse.ensureDirSync(src)
-          fse.mkdirsSync(path.join(src, 'some-folder'))
-          fs.writeFileSync(path.join(src, 'some-file'), 'hi')
-
-          // verify source has stuff
-          paths = fs.readdirSync(src)
-          assert(paths.indexOf('some-file') >= 0)
-          assert(paths.indexOf('some-folder') >= 0)
-
-          fse.move(src, dest, {clobber: true}, function(err) {
-            if (err) return done(err)
-
-            // verify dest does not have old stuff
-            var paths = fs.readdirSync(dest)
-            assert(paths.indexOf('a-file') == -1)
-            assert(paths.indexOf('a-folder') == -1)
-
-            // verify dest has new stuff
-            assert(paths.indexOf('some-file') >= 0)
-            assert(paths.indexOf('some-folder') >= 0)
-
-            done()
-          })
-        })
-      })
-    })
-  })
-})
-
-
-
-
diff --git a/test/ncp/fixtures/broken-symlink-fixtures/src/broken-symlink b/test/ncp/fixtures/broken-symlink-fixtures/src/broken-symlink
deleted file mode 120000
index cfa0a46..0000000
--- a/test/ncp/fixtures/broken-symlink-fixtures/src/broken-symlink
+++ /dev/null
@@ -1 +0,0 @@
-does-not-exist
\ No newline at end of file
diff --git a/test/ncp/fixtures/regular-fixtures/out/sub/z b/test/ncp/fixtures/regular-fixtures/out/sub/z
deleted file mode 100644
index cf291b5..0000000
--- a/test/ncp/fixtures/regular-fixtures/out/sub/z
+++ /dev/null
@@ -1 +0,0 @@
-Hello nodejitsu
diff --git a/test/ncp/fixtures/regular-fixtures/out/z b/test/ncp/fixtures/regular-fixtures/out/z
deleted file mode 100644
index 802992c..0000000
--- a/test/ncp/fixtures/regular-fixtures/out/z
+++ /dev/null
@@ -1 +0,0 @@
-Hello world
diff --git a/test/ncp/fixtures/symlink-fixtures/out/dir-symlink/bar b/test/ncp/fixtures/symlink-fixtures/out/dir-symlink/bar
deleted file mode 100644
index fd06f5f..0000000
--- a/test/ncp/fixtures/symlink-fixtures/out/dir-symlink/bar
+++ /dev/null
@@ -1 +0,0 @@
-bar contents
\ No newline at end of file
diff --git a/test/ncp/fixtures/symlink-fixtures/out/dir/bar b/test/ncp/fixtures/symlink-fixtures/out/dir/bar
deleted file mode 100644
index fd06f5f..0000000
--- a/test/ncp/fixtures/symlink-fixtures/out/dir/bar
+++ /dev/null
@@ -1 +0,0 @@
-bar contents
\ No newline at end of file
diff --git a/test/ncp/fixtures/symlink-fixtures/out/file-symlink b/test/ncp/fixtures/symlink-fixtures/out/file-symlink
deleted file mode 100644
index 35fc060..0000000
--- a/test/ncp/fixtures/symlink-fixtures/out/file-symlink
+++ /dev/null
@@ -1 +0,0 @@
-foo contents
\ No newline at end of file
diff --git a/test/ncp/fixtures/symlink-fixtures/out/foo b/test/ncp/fixtures/symlink-fixtures/out/foo
deleted file mode 100644
index 35fc060..0000000
--- a/test/ncp/fixtures/symlink-fixtures/out/foo
+++ /dev/null
@@ -1 +0,0 @@
-foo contents
\ No newline at end of file
diff --git a/test/ncp/fixtures/symlink-fixtures/src/dir-symlink b/test/ncp/fixtures/symlink-fixtures/src/dir-symlink
deleted file mode 120000
index 8724519..0000000
--- a/test/ncp/fixtures/symlink-fixtures/src/dir-symlink
+++ /dev/null
@@ -1 +0,0 @@
-dir
\ No newline at end of file
diff --git a/test/ncp/fixtures/symlink-fixtures/src/dir/bar b/test/ncp/fixtures/symlink-fixtures/src/dir/bar
deleted file mode 100644
index fd06f5f..0000000
--- a/test/ncp/fixtures/symlink-fixtures/src/dir/bar
+++ /dev/null
@@ -1 +0,0 @@
-bar contents
\ No newline at end of file
diff --git a/test/ncp/fixtures/symlink-fixtures/src/file-symlink b/test/ncp/fixtures/symlink-fixtures/src/file-symlink
deleted file mode 120000
index 1910281..0000000
--- a/test/ncp/fixtures/symlink-fixtures/src/file-symlink
+++ /dev/null
@@ -1 +0,0 @@
-foo
\ No newline at end of file
diff --git a/test/ncp/fixtures/symlink-fixtures/src/foo b/test/ncp/fixtures/symlink-fixtures/src/foo
deleted file mode 100644
index 35fc060..0000000
--- a/test/ncp/fixtures/symlink-fixtures/src/foo
+++ /dev/null
@@ -1 +0,0 @@
-foo contents
\ No newline at end of file
diff --git a/test/ncp/ncp.test.js b/test/ncp/ncp.test.js
deleted file mode 100644
index d9aecf5..0000000
--- a/test/ncp/ncp.test.js
+++ /dev/null
@@ -1,198 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var rimraf = require('rimraf')
-var readDirFiles = require('read-dir-files').read //temporary, will remove
-var util = require('util')
-var ncp = require('../../lib/_copy').ncp
-
-var fixturesDir = path.join(__dirname, 'fixtures')
-
-describe('ncp', function () {
-  describe('regular files and directories', function () {
-    var fixtures = path.join(fixturesDir, 'regular-fixtures'),
-        src = path.join(fixtures, 'src'),
-        out = path.join(fixtures, 'out')
-
-    before(function (cb) {
-      rimraf(out, function() {
-        ncp(src, out, cb)
-      })
-    })
-
-    describe('when copying a directory of files', function () {
-      it('files are copied correctly', function (cb) {
-        readDirFiles(src, 'utf8', function (srcErr, srcFiles) {
-          readDirFiles(out, 'utf8', function (outErr, outFiles) {
-            assert.ifError(srcErr)
-            assert.deepEqual(srcFiles, outFiles)
-            cb()
-          })
-        })
-      })
-    })
-
-    describe('when copying files using filter', function () {
-      before(function (cb) {
-        var filter = function(name) {
-          return name.substr(name.length - 1) != 'a'
-        }
-        rimraf(out, function () {
-          ncp(src, out, {filter: filter}, cb)
-        })
-      })
-
-      it('files are copied correctly', function (cb) {
-        readDirFiles(src, 'utf8', function (srcErr, srcFiles) {
-          function filter(files) {
-            for (var fileName in files) {
-              var curFile = files[fileName]
-              if (curFile instanceof Object)
-                return filter(curFile)
-              if (fileName.substr(fileName.length - 1) == 'a')
-                delete files[fileName]
-            }
-          }
-          filter(srcFiles)
-          readDirFiles(out, 'utf8', function (outErr, outFiles) {
-            assert.ifError(outErr)
-            assert.deepEqual(srcFiles, outFiles)
-            cb()
-          })
-        })
-      })
-    })
-
-    describe('when using clobber=false', function () {
-      it('the copy is completed successfully', function (cb) {
-        ncp(src, out, function() {
-          ncp(src, out, {clobber: false}, function(err) {
-            assert.ifError(err)
-            cb()
-          })
-        })
-      })
-    })
-
-    describe('when using transform', function () {
-      it('file descriptors are passed correctly', function (cb) {
-        ncp(src, out, {
-           transform: function(read,write,file) {
-              assert.notEqual(file.name, undefined)
-              assert.strictEqual(typeof file.mode,'number')
-              read.pipe(write)
-           }
-        }, cb)
-      })
-    })
-  })
-
-  describe('symlink handling', function () {
-    var fixtures = path.join(fixturesDir, 'symlink-fixtures'),
-        src = path.join(fixtures, 'src'),
-        out = path.join(fixtures, 'out')
-
-    beforeEach(function (cb) {
-      rimraf(out, cb)
-    })
-
-    it('copies symlinks by default', function (cb) {
-      ncp(src, out, function (err) {
-        if (err) return cb(err)
-        assert.equal(fs.readlinkSync(path.join(out, 'file-symlink')), 'foo')
-        assert.equal(fs.readlinkSync(path.join(out, 'dir-symlink')), 'dir')
-        cb()
-      })
-    })
-
-    it('copies file contents when dereference=true', function (cb) {
-      ncp(src, out, { dereference: true }, function (err) {
-        var fileSymlinkPath = path.join(out, 'file-symlink')
-        assert.ok(fs.lstatSync(fileSymlinkPath).isFile())
-        assert.equal(fs.readFileSync(fileSymlinkPath), 'foo contents')
-
-        var dirSymlinkPath = path.join(out, 'dir-symlink')
-        assert.ok(fs.lstatSync(dirSymlinkPath).isDirectory())
-        assert.deepEqual(fs.readdirSync(dirSymlinkPath), ['bar'])
-
-        cb()
-      })
-    })
-  })
-
-  describe('broken symlink handling', function () {
-    var fixtures = path.join(fixturesDir, 'broken-symlink-fixtures'),
-        src = path.join(fixtures, 'src'),
-        out = path.join(fixtures, 'out')
-
-    beforeEach(function (cb) {
-      rimraf(out, cb)
-    })
-
-    it('copies broken symlinks by default', function (cb) {
-      ncp(src, out, function (err) {
-        if (err) return cb(err)
-        assert.equal(fs.readlinkSync(path.join(out, 'broken-symlink')), 'does-not-exist')
-        cb()
-      })
-    })
-
-    it('returns an error when dereference=true', function (cb) {
-      ncp(src, out, {dereference: true}, function (err) {
-        assert.equal(err.length, 1)
-        assert.equal(err[0].code, 'ENOENT')
-        cb()
-      })
-    })
-  })
-
-  // see https://github.com/AvianFlu/ncp/issues/71
-  describe('Issue 71: Odd Async Behaviors', function(cb){
-    var fixtures = path.join(__dirname, 'fixtures', 'regular-fixtures')
-    var src = path.join(fixtures, 'src')
-    var out = path.join(fixtures, 'out')
-
-    var totalCallbacks = 0
-
-    function copyAssertAndCount(callback){
-      // rimraf(out, function() {
-        ncp(src, out, function(err){
-          totalCallbacks += 1
-          readDirFiles(src, 'utf8', function (srcErr, srcFiles) {
-            readDirFiles(out, 'utf8', function (outErr, outFiles) {
-              assert.ifError(srcErr)
-              assert.deepEqual(srcFiles, outFiles)
-              callback()
-            })
-          })
-        })
-      // })
-    }
-
-    describe('when copying a directory of files without cleaning the destination', function () {
-      it('callback fires once per run and directories are equal', function (done) {
-        var expected = 10
-        var count = 10
-
-        function next() {
-          if (count > 0) {
-            setTimeout(function(){
-              copyAssertAndCount(function() {
-                count -= 1
-                next()
-              })
-            }, 100)
-
-          } else {
-            // console.log('Total callback count is', totalCallbacks)
-            assert.equal(totalCallbacks, expected)
-            done()
-          }
-        }
-
-        next()
-
-      })
-    })
-  })
-})
diff --git a/test/readme.md b/test/readme.md
new file mode 100644
index 0000000..1ea7c5b
--- /dev/null
+++ b/test/readme.md
@@ -0,0 +1 @@
+Looking for the test files? You can find all of the test files in `lib/**/__tests__`.
\ No newline at end of file
diff --git a/test/remove.test.js b/test/remove.test.js
deleted file mode 100644
index 32d444d..0000000
--- a/test/remove.test.js
+++ /dev/null
@@ -1,124 +0,0 @@
-var assert = require('assert')
-var fs = require('fs')
-var path = require('path')
-var sr = require('secure-random')
-var testutil = require('testutil')
-var fse = require('../')
-
-/* global afterEach, beforeEach, describe, it */
-
-var TEST_DIR
-
-function buildFixtureDir () {
-  var buf = sr.randomBuffer(5)
-  var baseDir = path.join(TEST_DIR, 'TEST_fs-extra_remove-' + Date.now())
-
-  fs.mkdirSync(baseDir)
-  fs.writeFileSync(path.join(baseDir, Math.random() + ''), buf)
-  fs.writeFileSync(path.join(baseDir, Math.random() + ''), buf)
-
-  var subDir = path.join(TEST_DIR, Math.random() + '')
-  fs.mkdirSync(subDir)
-  fs.writeFileSync(path.join(subDir, Math.random() + ''))
-  return baseDir
-}
-
-describe('remove', function() {
-  beforeEach(function() {
-    TEST_DIR = testutil.createTestDir('fs-extra')
-  })
-
-  afterEach(function() {
-    if (fs.existsSync(TEST_DIR)) fse.removeSync(TEST_DIR)
-  })
-
-  describe('+ removeSync()', function() {
-    it('should delete directories and files synchronously', function() {
-      assert(fs.existsSync(TEST_DIR))
-      fse.removeSync(TEST_DIR)
-      assert(!fs.existsSync(TEST_DIR))
-    })
-
-    it('should delete an empty directory synchronously', function() {
-      assert(fs.existsSync(TEST_DIR))
-      fse.removeSync(TEST_DIR)
-      assert(!fs.existsSync(TEST_DIR))
-    })
-
-    it('should delete a file synchronously', function() {
-      var file = path.join(TEST_DIR, 'file')
-      fs.writeFileSync(file, 'hello')
-      assert(fs.existsSync(file))
-      fse.removeSync(file)
-      assert(!fs.existsSync(file))
-    })
-  })
-
-  describe('+ remove()', function() {
-    it('should delete an empty directory', function(done) {
-      assert(fs.existsSync(TEST_DIR))
-      fse.remove(TEST_DIR, function(err) {
-        assert.ifError(err)
-        assert(!fs.existsSync(TEST_DIR))
-        done()
-      })
-    })
-
-    it('should delete a directory full of directories and files', function(done) {
-      buildFixtureDir()
-      assert(fs.existsSync(TEST_DIR))
-      fse.remove(TEST_DIR, function(err) {
-        assert.ifError(err)
-        assert(!fs.existsSync(TEST_DIR))
-        done()
-      })
-    })
-
-    it('should delete a file', function(done) {
-      var file = path.join(TEST_DIR, 'file')
-      fs.writeFileSync(file, 'hello')
-
-      assert(fs.existsSync(file))
-      fse.remove(file, function(err) {
-        assert.ifError(err)
-        assert(!fs.existsSync(file))
-        done()
-      })
-    })
-
-    it('should delete without a callback', function(done) {
-      var file = path.join(TEST_DIR, 'file')
-      fs.writeFileSync(file, 'hello')
-
-      assert(fs.existsSync(file))
-      var existsChecker = setInterval(function() {
-        fs.exists(file, function(itDoes) {
-          if (!itDoes) {
-            clearInterval(existsChecker)
-            done()
-          }
-        })
-      }, 25)
-      fse.remove(file)
-    })
-  })
-
-  describe('+ delete()', function() {
-    it('should delete an empty directory', function(done) {
-      assert(fs.existsSync(TEST_DIR))
-      fse['delete'](TEST_DIR, function(err) {
-        assert.ifError(err)
-        assert(!fs.existsSync(TEST_DIR))
-        done()
-      })
-    })
-  })
-
-  describe('+ deleteSync()', function() {
-    it('should delete directories and files synchronously', function() {
-      assert(fs.existsSync(TEST_DIR))
-      fse.deleteSync(TEST_DIR)
-      assert(!fs.existsSync(TEST_DIR))
-    })
-  })
-})

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-fs-extra.git



More information about the Pkg-javascript-commits mailing list