@jsenv/filesystem 4.8.2 → 4.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
package/src/main.js
CHANGED
|
@@ -59,7 +59,10 @@ export { writeEntryModificationTimeSync } from "./read_write/stat/write_entry_mo
|
|
|
59
59
|
|
|
60
60
|
// move
|
|
61
61
|
export { moveDirectoryContent } from "./move/move_directory_content.js";
|
|
62
|
+
export { moveFile } from "./move/move_file.js";
|
|
62
63
|
export { moveEntry } from "./move/move_entry.js";
|
|
64
|
+
export { moveFileSync } from "./move/move_file_sync.js";
|
|
65
|
+
export { moveEntrySync } from "./move/move_entry_sync.js";
|
|
63
66
|
|
|
64
67
|
// copy
|
|
65
68
|
export { copyEntry } from "./copy/copy_entry.js";
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { resolveUrl, urlToFileSystemPath } from "@jsenv/urls";
|
|
2
|
+
import { renameSync } from "node:fs";
|
|
3
|
+
|
|
4
|
+
import { copyEntry } from "../copy/copy_entry.js";
|
|
5
|
+
import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
|
|
6
|
+
import { urlTargetsSameFileSystemPath } from "../path_and_url/url_targets_same_file_system_path.js";
|
|
7
|
+
import { ensureParentDirectoriesSync } from "../read_write/ensure_parent_directories_sync.js";
|
|
8
|
+
import { readSymbolicLinkSync } from "../read_write/read_symbolic_link_sync.js";
|
|
9
|
+
import { readEntryStatSync } from "../read_write/stat/read_entry_stat_sync.js";
|
|
10
|
+
import { statsToType } from "../read_write/stat/stats_to_type.js";
|
|
11
|
+
import { removeEntrySync } from "../remove/remove_entry_sync.js";
|
|
12
|
+
|
|
13
|
+
export const moveEntrySync = ({
|
|
14
|
+
from,
|
|
15
|
+
to,
|
|
16
|
+
overwrite = false,
|
|
17
|
+
allowUseless = false,
|
|
18
|
+
followLink = true,
|
|
19
|
+
}) => {
|
|
20
|
+
const fromUrl = assertAndNormalizeFileUrl(from);
|
|
21
|
+
const fromPath = urlToFileSystemPath(fromUrl);
|
|
22
|
+
let toUrl = assertAndNormalizeFileUrl(to);
|
|
23
|
+
let toPath = urlToFileSystemPath(toUrl);
|
|
24
|
+
|
|
25
|
+
const sourceStats = readEntryStatSync(fromUrl, {
|
|
26
|
+
nullIfNotFound: true,
|
|
27
|
+
followLink: false,
|
|
28
|
+
});
|
|
29
|
+
if (!sourceStats) {
|
|
30
|
+
throw new Error(`nothing to move from ${fromPath}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let destinationStats = readEntryStatSync(toUrl, {
|
|
34
|
+
nullIfNotFound: true,
|
|
35
|
+
// we force false here but in fact we will follow the destination link
|
|
36
|
+
// to know where we will actually move and detect useless move overrite etc..
|
|
37
|
+
followLink: false,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (followLink && destinationStats && destinationStats.isSymbolicLink()) {
|
|
41
|
+
const linkTarget = readSymbolicLinkSync(toUrl);
|
|
42
|
+
toUrl = resolveUrl(linkTarget, toUrl);
|
|
43
|
+
toPath = urlToFileSystemPath(toUrl);
|
|
44
|
+
destinationStats = readEntryStatSync(toUrl, {
|
|
45
|
+
nullIfNotFound: true,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (urlTargetsSameFileSystemPath(fromUrl, toUrl)) {
|
|
50
|
+
if (allowUseless) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
throw new Error(
|
|
54
|
+
`no move needed for ${fromPath} because destination and source are the same`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (destinationStats) {
|
|
59
|
+
const sourceType = statsToType(sourceStats);
|
|
60
|
+
const destinationType = statsToType(destinationStats);
|
|
61
|
+
|
|
62
|
+
if (sourceType !== destinationType) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`cannot move ${sourceType} from ${fromPath} to ${toPath} because destination exists and is not a ${sourceType} (it's a ${destinationType})`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
if (!overwrite) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`cannot move ${sourceType} from ${fromPath} to ${toPath} because destination exists and overwrite option is disabled`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// remove file, link, directory...
|
|
74
|
+
removeEntrySync(toUrl, {
|
|
75
|
+
recursive: true,
|
|
76
|
+
});
|
|
77
|
+
} else {
|
|
78
|
+
ensureParentDirectoriesSync(toUrl);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
moveNaiveSync(fromPath, toPath, {
|
|
82
|
+
handleCrossDeviceError: () => {
|
|
83
|
+
copyEntry({
|
|
84
|
+
from: fromUrl,
|
|
85
|
+
to: toUrl,
|
|
86
|
+
preserveStat: true,
|
|
87
|
+
});
|
|
88
|
+
removeEntrySync(fromUrl, {
|
|
89
|
+
recursive: true,
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const moveNaiveSync = (
|
|
96
|
+
fromPath,
|
|
97
|
+
destinationPath,
|
|
98
|
+
{ handleCrossDeviceError = null } = {},
|
|
99
|
+
) => {
|
|
100
|
+
try {
|
|
101
|
+
renameSync(fromPath, destinationPath);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
if (error) {
|
|
104
|
+
if (handleCrossDeviceError && error.code === "EXDEV") {
|
|
105
|
+
handleCrossDeviceError(error);
|
|
106
|
+
} else {
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|