UNPKG

3.58 kBJavaScriptView Raw
1var fs = require('fs');
2var path = require('path');
3var common = require('./common');
4var cp = require('./cp');
5var rm = require('./rm');
6
7common.register('mv', _mv, {
8 cmdOptions: {
9 'f': '!no_force',
10 'n': 'no_force',
11 },
12});
13
14// Checks if cureent file was created recently
15function checkRecentCreated(sources, index) {
16 var lookedSource = sources[index];
17 return sources.slice(0, index).some(function (src) {
18 return path.basename(src) === path.basename(lookedSource);
19 });
20}
21
22//@
23//@ ### mv([options ,] source [, source ...], dest')
24//@ ### mv([options ,] source_array, dest')
25//@
26//@ Available options:
27//@
28//@ + `-f`: force (default behavior)
29//@ + `-n`: no-clobber
30//@
31//@ Examples:
32//@
33//@ ```javascript
34//@ mv('-n', 'file', 'dir/');
35//@ mv('file1', 'file2', 'dir/');
36//@ mv(['file1', 'file2'], 'dir/'); // same as above
37//@ ```
38//@
39//@ Moves `source` file(s) to `dest`.
40function _mv(options, sources, dest) {
41 // Get sources, dest
42 if (arguments.length < 3) {
43 common.error('missing <source> and/or <dest>');
44 } else if (arguments.length > 3) {
45 sources = [].slice.call(arguments, 1, arguments.length - 1);
46 dest = arguments[arguments.length - 1];
47 } else if (typeof sources === 'string') {
48 sources = [sources];
49 } else {
50 // TODO(nate): figure out if we actually need this line
51 common.error('invalid arguments');
52 }
53
54 var exists = fs.existsSync(dest);
55 var stats = exists && common.statFollowLinks(dest);
56
57 // Dest is not existing dir, but multiple sources given
58 if ((!exists || !stats.isDirectory()) && sources.length > 1) {
59 common.error('dest is not a directory (too many sources)');
60 }
61
62 // Dest is an existing file, but no -f given
63 if (exists && stats.isFile() && options.no_force) {
64 common.error('dest file already exists: ' + dest);
65 }
66
67 sources.forEach(function (src, srcIndex) {
68 if (!fs.existsSync(src)) {
69 common.error('no such file or directory: ' + src, { continue: true });
70 return; // skip file
71 }
72
73 // If here, src exists
74
75 // When copying to '/path/dir':
76 // thisDest = '/path/dir/file1'
77 var thisDest = dest;
78 if (fs.existsSync(dest) && common.statFollowLinks(dest).isDirectory()) {
79 thisDest = path.normalize(dest + '/' + path.basename(src));
80 }
81
82 var thisDestExists = fs.existsSync(thisDest);
83
84 if (thisDestExists && checkRecentCreated(sources, srcIndex)) {
85 // cannot overwrite file created recently in current execution, but we want to continue copying other files
86 if (!options.no_force) {
87 common.error("will not overwrite just-created '" + thisDest + "' with '" + src + "'", { continue: true });
88 }
89 return;
90 }
91
92 if (fs.existsSync(thisDest) && options.no_force) {
93 common.error('dest file already exists: ' + thisDest, { continue: true });
94 return; // skip file
95 }
96
97 if (path.resolve(src) === path.dirname(path.resolve(thisDest))) {
98 common.error('cannot move to self: ' + src, { continue: true });
99 return; // skip file
100 }
101
102 try {
103 fs.renameSync(src, thisDest);
104 } catch (e) {
105 /* istanbul ignore next */
106 if (e.code === 'EXDEV') {
107 // If we're trying to `mv` to an external partition, we'll actually need
108 // to perform a copy and then clean up the original file. If either the
109 // copy or the rm fails with an exception, we should allow this
110 // exception to pass up to the top level.
111 cp('-r', src, thisDest);
112 rm('-rf', src);
113 }
114 }
115 }); // forEach(src)
116 return '';
117} // mv
118module.exports = _mv;