{"id":11374,"library":"mv","title":"mv (Node.js)","description":"The `mv` package by `andrewrk` provides a robust utility for moving files and directories in Node.js, specifically addressing the limitation of `fs.rename` which cannot perform operations across different devices or file systems. It first attempts a standard `fs.rename` and, upon failure (e.g., cross-device boundary), transparently falls back to a copy-then-unlink strategy. For files, this involves piping data, and for directories, it utilizes a recursive copy (`ncp`) followed by removal of the source (`rimraf`). It supports options like automatically creating destination parent directories (`mkdirp: true`) and preventing overwrites (`clobber: false`). The current stable version, `2.1.1`, was released in 2015. Given its age and lack of recent updates, this package is considered abandoned. Developers are advised to consider actively maintained alternatives, such as `move-file`, which offer modern Promise-based APIs and enhanced ESM support, if possible.","status":"abandoned","version":"2.1.1","language":"javascript","source_language":"en","source_url":"git://github.com/andrewrk/node-mv","tags":["javascript","mv","move","rename","device","recursive","folder"],"install":[{"cmd":"npm install mv","lang":"bash","label":"npm"},{"cmd":"yarn add mv","lang":"bash","label":"yarn"},{"cmd":"pnpm add mv","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Used internally to create destination directories when `mkdirp: true` option is provided.","package":"mkdirp","optional":false},{"reason":"Used internally for recursively copying directories during cross-device moves.","package":"ncp","optional":false},{"reason":"Used internally for recursively removing source directories after successful cross-device moves.","package":"rimraf","optional":false}],"imports":[{"note":"This package is CommonJS-only and uses a callback-based API. Direct named ESM imports will not work.","wrong":"import { mv } from 'mv';","symbol":"mv","correct":"const mv = require('mv');"},{"note":"When importing a CommonJS module into an ESM context, its `module.exports` object becomes the default export. Named exports are not reliably available.","wrong":"import { mv } from 'mv';","symbol":"mv (ESM interop)","correct":"import mv from 'mv';"},{"note":"Dynamic `import()` can be used in both CommonJS and ESM to load CommonJS modules. The CJS `module.exports` is exposed as the default export.","symbol":"mv (dynamic import)","correct":"const mv = await import('mv').then(mod => mod.default || mod);"}],"quickstart":{"code":"const mv = require('mv');\nconst fs = require('fs');\nconst path = require('path');\n\nasync function runExample() {\n  const sourceFile = path.join(__dirname, 'source.txt');\n  const destFile = path.join(__dirname, 'dest', 'new-name.txt');\n  const sourceDir = path.join(__dirname, 'old-dir');\n  const destDir = path.join(__dirname, 'target', 'new-dir');\n\n  // Ensure directories exist for testing\n  fs.mkdirSync(path.join(__dirname, 'dest'), { recursive: true });\n  fs.mkdirSync(sourceDir, { recursive: true });\n  fs.writeFileSync(sourceFile, 'Hello from source file!');\n  fs.writeFileSync(path.join(sourceDir, 'file-in-dir.txt'), 'Content in directory!');\n\n  console.log('Moving a file...');\n  await new Promise((resolve, reject) => {\n    mv(sourceFile, destFile, function(err) {\n      if (err) return reject(err);\n      console.log('File moved successfully to', destFile);\n      resolve();\n    });\n  });\n\n  console.log('\\nMoving a directory and creating parent path...');\n  await new Promise((resolve, reject) => {\n    mv(sourceDir, destDir, { mkdirp: true }, function(err) {\n      if (err) return reject(err);\n      console.log('Directory moved successfully to', destDir);\n      resolve();\n    });\n  });\n\n  const existingFile = path.join(__dirname, 'existing.txt');\n  fs.writeFileSync(existingFile, 'This file exists.');\n  const clashTarget = path.join(__dirname, 'existing.txt'); // Intentionally clash\n  fs.writeFileSync(clashTarget, 'This will be clobbered unless clobber:false.');\n\n  console.log('\\nAttempting to move file with clobber: false (expecting EEXIST error)...');\n  await new Promise((resolve) => {\n    mv(sourceFile, clashTarget, { clobber: false }, function(err) {\n      if (err) {\n        console.error('Expected error when clobber: false:', err.code, err.message);\n        // Clean up potentially clobbered file for next run if it wasn't the exact clash\n        if (fs.existsSync(clashTarget)) fs.unlinkSync(clashTarget);\n        resolve();\n      } else {\n        console.log('File moved without error when clobber: false (unexpected for this example).');\n        resolve();\n      }\n    });\n  }).catch(console.error);\n\n  // Clean up created files/dirs\n  if (fs.existsSync(sourceFile)) fs.unlinkSync(sourceFile);\n  if (fs.existsSync(destFile)) fs.unlinkSync(destFile);\n  if (fs.existsSync(sourceDir)) fs.rmSync(sourceDir, { recursive: true, force: true });\n  if (fs.existsSync(destDir)) fs.rmSync(destDir, { recursive: true, force: true });\n  if (fs.existsSync(path.join(__dirname, 'dest'))) fs.rmdirSync(path.join(__dirname, 'dest'));\n  if (fs.existsSync(path.join(__dirname, 'target'))) fs.rmdirSync(path.join(__dirname, 'target'));\n  if (fs.existsSync(existingFile)) fs.unlinkSync(existingFile);\n}\n\nrunExample().catch(console.error);\n","lang":"javascript","description":"Demonstrates moving a file, moving a directory (with `mkdirp`), and handling the `EEXIST` error when attempting to overwrite a file with `clobber: false`."},"warnings":[{"fix":"Migrate to a modern, actively maintained file moving utility such as `move-file` (which offers a Promise-based API) or other alternatives like `fs-extra`'s `move` function.","message":"This package is officially abandoned and has not been updated since 2015 (v2.1.1). It is not recommended for new projects and should be replaced in existing ones to mitigate potential issues.","severity":"breaking","affected_versions":">=2.1.1"},{"fix":"Replace `mv` with a modern alternative that uses up-to-date and secure dependencies, or implement custom logic using native `fs` module functions (`fs.rename`, `fs.cp`, `fs.rm`) and ensuring proper error handling for cross-device moves.","message":"The internal dependencies `mkdirp`, `ncp`, and `rimraf` used by `mv` are likely very old versions (e.g., `mkdirp@0.5.1`, `rimraf@2.4.0`). These old versions are known to have security vulnerabilities (CVEs) and may not behave correctly with newer Node.js versions or file system features.","severity":"breaking","affected_versions":">=2.1.1"},{"fix":"Wrap `mv` calls in a Promise-based function (`util.promisify` can be used for this) if integrating into an `async/await` codebase. Ideally, migrate to a Promise-based alternative for file operations.","message":"`mv` uses an asynchronous, callback-based API. Modern Node.js development predominantly uses Promises and `async/await`. Mixing callback-based code with Promises can lead to 'callback hell' or unhandled promise rejections if not carefully managed.","severity":"gotcha","affected_versions":">=2.1.1"},{"fix":"Be aware of performance implications for large files/directories. If performance is critical, consider pre-checking if source and destination are on the same device to choose the most efficient method, or use `mv` as-is, accepting the fallback overhead.","message":"When `mv` performs a cross-device move, it defaults to a copy-then-unlink strategy. For very large files or directories, this operation can be significantly slower and more resource-intensive (doubling disk space usage temporarily) than a direct `fs.rename`.","severity":"gotcha","affected_versions":">=2.1.1"},{"fix":"Implement explicit error handling for `EEXIST` when `clobber: false` is used. Check for the error code and decide whether to ignore, log, or stop the operation based on application logic.","message":"Using the `clobber: false` option will cause `mv` to return an error with `err.code === 'EEXIST'` if the destination file or directory already exists. It does not provide an option to automatically skip the move without an error.","severity":"gotcha","affected_versions":">=2.1.1"}],"env_vars":null,"last_verified":"2026-04-19T00:00:00.000Z","next_check":"2026-07-18T00:00:00.000Z","problems":[{"fix":"Either remove the existing destination file/directory, set `clobber: true` (which is the default behavior if not specified) to overwrite, or handle the `EEXIST` error explicitly in your callback.","cause":"Attempting to move a file or directory to a path that already exists when `clobber: false` option is used.","error":"Error: EEXIST: file already exists, 'dest/file'"},{"fix":"Verify that the `source` path is correct and that the file/directory actually exists before calling `mv`. Use `fs.existsSync()` or `fs.promises.access()` to check.","cause":"The source file or directory specified for the move operation does not exist at the given path.","error":"Error: ENOENT: no such file or directory, open 'source/file'"},{"fix":"Ensure the Node.js process has appropriate file system permissions for the paths involved. Run the process with elevated privileges if necessary (e.g., `sudo node your_script.js` on Linux/macOS, or 'Run as Administrator' on Windows), or adjust directory/file permissions.","cause":"The Node.js process lacks the necessary read, write, or execute permissions for the source, destination, or an intermediate directory.","error":"Error: EACCES: permission denied, rename 'source/file' -> 'dest/file' (or EPERM)"},{"fix":"Ensure `const mv = require('mv');` is correctly placed and executed. If using ESM, make sure to use `import mv from 'mv';` (for CommonJS default export) or dynamic `import('mv')`.","cause":"The `mv` function was not correctly imported or required, or a different module or object without an `mv` method is being referenced.","error":"TypeError: mv is not a function"}],"ecosystem":"npm"}