Repository: jprichardson/node-fs-extra
Pull request #954 (2022-04-08 15:52:42)

refactor: replace console.warn with process.emitWarning
To have consistent warnings from https://github.com/jprichardson/node-fs-extra/pull/953#issuecomment-1092663914 by @JPeer264 
> Yes `process.emitWarning` sounds also like a great option to add (just my opinion on it). However, a separate PR would be nice if you want to go the extra mile as we squash our PRs. @manidlou @RyanZim @jprichardson any objections?

Prints out via `process.emitWarning` in the following format:

![image](https://user-images.githubusercontent.com/29530649/162500584-a6ac7a4a-3df2-4ca4-b657-ca78878744be.png)



RyanZim: Should we consider this: https://nodejs.org/api/process.html#avoiding-duplicate-warnings?
peteriman: I'm not exactly sure, as my motive was to remain the behavior of the `console.warn` which warns duplicatedly if called multiple times.

Should we change it to use  https://nodejs.org/api/process.html#avoiding-duplicate-warnings instead, so it only warns on the first time?

What do you guy think?
@RyanZim @JPeer264 @manidlou @jprichardson 

Patch:
diff --git a/lib/copy/copy-sync.js b/lib/copy/copy-sync.js
index 5e245b9..551abe0 100644
--- a/lib/copy/copy-sync.js
+++ b/lib/copy/copy-sync.js
@@ -17,8 +17,11 @@ function copySync (src, dest, opts) {
 
   // Warn about using preserveTimestamps on 32-bit node
   if (opts.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`)
+    process.emitWarning(
+      'Using the preserveTimestamps option in 32-bit node is not recommended;\n\n' +
+      '\tsee https://github.com/jprichardson/node-fs-extra/issues/269',
+      'Warning', 'fs-extra-WARN0002'
+    )
   }
 
   const { srcStat, destStat } = stat.checkPathsSync(src, dest, 'copy', opts)
diff --git a/lib/copy/copy.js b/lib/copy/copy.js
index 2a2874f..09d53df 100644
--- a/lib/copy/copy.js
+++ b/lib/copy/copy.js
@@ -23,8 +23,11 @@ function copy (src, dest, opts, cb) {
 
   // Warn about using preserveTimestamps on 32-bit node
   if (opts.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`)
+    process.emitWarning(
+      'Using the preserveTimestamps option in 32-bit node is not recommended;\n\n' +
+      '\tsee https://github.com/jprichardson/node-fs-extra/issues/269',
+      'Warning', 'fs-extra-WARN0001'
+    )
   }
 
   stat.checkPaths(src, dest, 'copy', opts, (err, stats) => {
diff --git a/lib/fs/index.js b/lib/fs/index.js
index e460b74..7b025e2 100644
--- a/lib/fs/index.js
+++ b/lib/fs/index.js
@@ -54,7 +54,6 @@ Object.assign(exports, fs)
 api.forEach(method => {
   exports[method] = u(fs[method])
 })
-exports.realpath.native = u(fs.realpath.native)
 
 // We differ from mz/fs in that we still ship the old, broken, fs.exists()
 // since we are a drop-in replacement for the native module
@@ -117,3 +116,13 @@ if (typeof fs.writev === 'function') {
     })
   }
 }
+
+// fs.realpath.native sometimes not available if fs is monkey-patched
+if (typeof fs.realpath.native === 'function') {
+  exports.realpath.native = u(fs.realpath.native)
+} else {
+  process.emitWarning(
+    'fs.realpath.native is not a function. Is fs being monkey-patched?',
+    'Warning', 'fs-extra-WARN0003'
+  )
+}
diff --git a/lib/move/move.js b/lib/move/move.js
index e9523e6..7dc6ecd 100644
--- a/lib/move/move.js
+++ b/lib/move/move.js
@@ -14,6 +14,8 @@ function move (src, dest, opts, cb) {
     opts = {}
   }
 
+  opts = opts || {}
+
   const overwrite = opts.overwrite || opts.clobber || false
 
   stat.checkPaths(src, dest, 'move', opts, (err, stats) => {


Test patch:
diff --git a/lib/__tests__/promise.test.js b/lib/__tests__/promise.test.js
index 31228bd..92a7a27 100644
--- a/lib/__tests__/promise.test.js
+++ b/lib/__tests__/promise.test.js
@@ -10,7 +10,6 @@ const methods = [
   'ensureFile',
   'ensureDir',
   'mkdirs',
-  'move',
   'readJson',
   'readJSON',
   'remove'
diff --git a/lib/fs/__tests__/realpath.test.js b/lib/fs/__tests__/realpath.test.js
index 9306859..b03249b 100644
--- a/lib/fs/__tests__/realpath.test.js
+++ b/lib/fs/__tests__/realpath.test.js
@@ -1,11 +1,67 @@
 'use strict'
 
-const fse = require('../..')
+const fs = require('fs')
+const path = require('path')
 const assert = require('assert')
 
 /* eslint-env mocha */
 
+describe('realpath.native does not exist', () => {
+  let warning
+  const warningListener = error => {
+    if (error.name === 'Warning') {
+      if (error.code.startsWith('fs-extra-WARN0003')) {
+        warning = error
+      }
+    }
+  }
+
+  const realpathNativeBackup = fs.realpath.native
+  const clearFseCache = () => {
+    const fsePath = path.dirname(require.resolve('../../..'))
+    for (const entry in require.cache) {
+      if (entry.startsWith(fsePath)) {
+        delete require.cache[entry]
+      }
+    }
+  }
+
+  before(() => {
+    process.on('warning', warningListener)
+
+    // clear existing require.cache
+    clearFseCache()
+
+    // simulate fs monkey-patch
+    delete fs.realpath.native
+  })
+
+  after(() => {
+    process.off('warning', warningListener)
+
+    // clear stubbed require.cache
+    clearFseCache()
+
+    // reinstate fs.realpath.native
+    fs.realpath.native = realpathNativeBackup
+  })
+
+  it('fse should not export realpath.native', done => {
+    const fse = require('../..')
+
+    // next event loop to allow event emitter/listener to happen
+    setImmediate(() => {
+      assert(warning, 'fs-extra-WARN0003 should be emitted')
+      done()
+    })
+
+    assert(!fse.realpath.native)
+  })
+})
+
 describe('realpath.native', () => {
+  const fse = require('../..')
+
   it('works with callbacks', () => {
     fse.realpath.native(__dirname, (err, path) => {
       assert.ifError(err)
diff --git a/lib/move/__tests__/move.test.js b/lib/move/__tests__/move.test.js
index d20f530..e7841ca 100644
--- a/lib/move/__tests__/move.test.js
+++ b/lib/move/__tests__/move.test.js
@@ -140,6 +140,17 @@ describe('+ move()', () => {
       })
     })
 
+    it('should support promises', async () => {
+      const src = path.join(TEST_DIR, 'a-file')
+      const dest = path.join(TEST_DIR, 'a-file-dest')
+
+      await fse.move(src, dest)
+
+      const contents = fs.readFileSync(dest, 'utf8')
+      const expected = /^sonic the hedgehog\r?\n$/
+      assert.ok(contents.match(expected))
+    })
+
     it('should not move a file if source and destination are the same', done => {
       const src = path.join(TEST_DIR, 'a-file')
       const dest = src
@@ -273,6 +284,34 @@ describe('+ move()', () => {
     })
   })
 
+  describe('> when opts is explicit undefined', () => {
+    it('works with callbacks', done => {
+      const src = path.join(TEST_DIR, 'a-file')
+      const dest = path.join(TEST_DIR, 'a-file-dest')
+
+      fse.move(src, dest, undefined, err => {
+        assert.ifError(err)
+        fs.readFile(dest, 'utf8', (err, contents) => {
+          const expected = /^sonic the hedgehog\r?\n$/
+          assert.ifError(err)
+          assert.ok(contents.match(expected))
+          done()
+        })
+      })
+    })
+
+    it('works with promises', async () => {
+      const src = path.join(TEST_DIR, 'a-file')
+      const dest = path.join(TEST_DIR, 'a-file-dest')
+
+      await fse.move(src, dest, undefined)
+
+      const contents = fs.readFileSync(dest, 'utf8')
+      const expected = /^sonic the hedgehog\r?\n$/
+      assert.ok(contents.match(expected))
+    })
+  })
+
   describeIfWindows('> when dest parent is root', () => {
     let dest