@jsenv/filesystem 4.3.1 → 4.4.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.
Files changed (66) hide show
  1. package/package.json +4 -3
  2. package/src/{assertDirectoryPresence.js → assert_directory_presence.js} +3 -3
  3. package/src/{assertFilePresence.js → assert_file_presence.js} +3 -3
  4. package/src/{copyDirectoryContent.js → copy/copy_directory_content.js} +7 -7
  5. package/src/copy/copy_directory_content_sync.js +90 -0
  6. package/src/copy/copy_directory_sync.js +9 -0
  7. package/src/{copyEntry.js → copy/copy_entry.js} +13 -13
  8. package/src/copy/copy_entry_sync.js +212 -0
  9. package/src/{registerDirectoryLifecycle.js → lifecycle/register_directory_lifecycle.js} +10 -9
  10. package/src/{registerFileLifecycle.js → lifecycle/register_file_lifecycle.js} +5 -5
  11. package/src/{collectDirectoryMatchReport.js → list/collect_directory_match_report.js} +5 -5
  12. package/src/{collectFiles.js → list/collect_files.js} +4 -4
  13. package/src/{listFilesMatching.js → list/list_files_matching.js} +1 -1
  14. package/src/main.js +78 -35
  15. package/src/{moveDirectoryContent.js → move/move_directory_content.js} +7 -7
  16. package/src/{moveEntry.js → move/move_entry.js} +8 -8
  17. package/src/{ensureParentDirectories.js → read_write/ensure_parent_directories.js} +3 -3
  18. package/src/read_write/ensure_parent_directories_sync.js +16 -0
  19. package/src/{readDirectory.js → read_write/read_directory.js} +2 -1
  20. package/src/read_write/read_directory_sync.js +10 -0
  21. package/src/{readFile.js → read_write/read_file.js} +1 -1
  22. package/src/read_write/read_file_structure_sync.js +57 -0
  23. package/src/{readFileSync.js → read_write/read_file_sync.js} +1 -1
  24. package/src/{readSymbolicLink.js → read_write/read_symbolic_link.js} +1 -1
  25. package/src/read_write/read_symbolic_link_sync.js +17 -0
  26. package/src/{grantPermissionsOnEntry.js → read_write/stat/grant_permissions_on_entry.js} +3 -3
  27. package/src/{internal → read_write/stat}/read_entry_info.js +1 -1
  28. package/src/{readEntryModificationTime.js → read_write/stat/read_entry_modification_time.js} +1 -1
  29. package/src/{readEntryPermissions.js → read_write/stat/read_entry_permissions.js} +2 -2
  30. package/src/{readEntryStat.js → read_write/stat/read_entry_stat.js} +2 -2
  31. package/src/read_write/stat/read_entry_stat_sync.js +92 -0
  32. package/src/{testEntryPermissions.js → read_write/stat/test_entry_permissions.js} +1 -1
  33. package/src/{writeEntryModificationTime.js → read_write/stat/write_entry_modification_time.js} +1 -1
  34. package/src/read_write/stat/write_entry_modification_time_sync.js +14 -0
  35. package/src/{writeEntryPermissions.js → read_write/stat/write_entry_permissions.js} +2 -2
  36. package/src/read_write/stat/write_entry_permissions_sync.js +77 -0
  37. package/src/{writeDirectory.js → read_write/write_directory.js} +3 -3
  38. package/src/read_write/write_directory_sync.js +42 -0
  39. package/src/{writeFile.js → read_write/write_file.js} +2 -2
  40. package/src/read_write/write_file_structure_sync.js +13 -0
  41. package/src/{writeFileSync.js → read_write/write_file_sync.js} +1 -1
  42. package/src/{writeSymbolicLink.js → read_write/write_symbolic_link.js} +5 -5
  43. package/src/read_write/write_symbolic_link_sync.js +115 -0
  44. package/src/remove/clear_directory_sync.js +68 -0
  45. package/src/{ensureEmptyDirectory.js → remove/ensure_empty_directory.js} +9 -7
  46. package/src/remove/ensure_empty_directory_sync.js +34 -0
  47. package/src/remove/remove_directory.js +8 -0
  48. package/src/remove/remove_directory_sync.js +8 -0
  49. package/src/{removeEntry.js → remove/remove_entry.js} +3 -3
  50. package/src/remove/remove_entry_sync.js +240 -0
  51. package/src/remove/remove_file.js +5 -0
  52. package/src/remove/remove_file_sync.js +5 -0
  53. package/src/internal/getCommandArgument.js +0 -27
  54. /package/src/{bufferToEtag.js → buffer_to_etag.js} +0 -0
  55. /package/src/{internal/createWatcher.js → lifecycle/create_watcher.js} +0 -0
  56. /package/src/{internal → lifecycle}/guard_second_call.js +0 -0
  57. /package/src/{internal → lifecycle}/track_resources.js +0 -0
  58. /package/src/{comparePathnames.js → path_and_url/compare_pathnames.js} +0 -0
  59. /package/src/{directory_url_validation.js → path_and_url/directory_url_validation.js} +0 -0
  60. /package/src/{ensureWindowsDriveLetter.js → path_and_url/ensure_windows_drive_letter.js} +0 -0
  61. /package/src/{fileSystemRootUrl.js → path_and_url/file_system_root_url.js} +0 -0
  62. /package/src/{file_url_validation.js → path_and_url/file_url_validation.js} +0 -0
  63. /package/src/{internal/urlTargetsSameFileSystemPath.js → path_and_url/url_targets_same_file_system_path.js} +0 -0
  64. /package/src/{getRealFileSystemUrlSync.js → read_write/get_real_file_system_url_sync.js} +0 -0
  65. /package/src/{internal → read_write/stat}/permissions.js +0 -0
  66. /package/src/{internal/statsToType.js → read_write/stat/stats_to_type.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/filesystem",
3
- "version": "4.3.1",
3
+ "version": "4.4.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -33,7 +33,8 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "@jsenv/urls": "2.2.1",
36
- "@jsenv/url-meta": "8.1.0",
37
- "@jsenv/abort": "4.2.4"
36
+ "@jsenv/url-meta": "8.2.0",
37
+ "@jsenv/abort": "4.2.4",
38
+ "@jsenv/utils": "2.0.1"
38
39
  }
39
40
  }
@@ -1,8 +1,8 @@
1
1
  import { urlToFileSystemPath } from "@jsenv/urls";
2
2
 
3
- import { assertAndNormalizeFileUrl } from "./file_url_validation.js";
4
- import { statsToType } from "./internal/statsToType.js";
5
- import { readEntryStat } from "./readEntryStat.js";
3
+ import { assertAndNormalizeFileUrl } from "./path_and_url/file_url_validation.js";
4
+ import { readEntryStat } from "./read_write/stat/read_entry_stat.js";
5
+ import { statsToType } from "./read_write/stat/stats_to_type.js";
6
6
 
7
7
  export const assertDirectoryPresence = async (source) => {
8
8
  const sourceUrl = assertAndNormalizeFileUrl(source);
@@ -1,8 +1,8 @@
1
1
  import { urlToFileSystemPath } from "@jsenv/urls";
2
2
 
3
- import { assertAndNormalizeFileUrl } from "./file_url_validation.js";
4
- import { statsToType } from "./internal/statsToType.js";
5
- import { readEntryStat } from "./readEntryStat.js";
3
+ import { assertAndNormalizeFileUrl } from "./path_and_url/file_url_validation.js";
4
+ import { readEntryStat } from "./read_write/stat/read_entry_stat.js";
5
+ import { statsToType } from "./read_write/stat/stats_to_type.js";
6
6
 
7
7
  export const assertFilePresence = async (source) => {
8
8
  const sourceUrl = assertAndNormalizeFileUrl(source);
@@ -1,13 +1,13 @@
1
1
  import { Abort } from "@jsenv/abort";
2
2
  import { urlToFileSystemPath, resolveUrl } from "@jsenv/urls";
3
3
 
4
- import { assertAndNormalizeDirectoryUrl } from "./directory_url_validation.js";
5
- import { readEntryStat } from "./readEntryStat.js";
6
- import { readSymbolicLink } from "./readSymbolicLink.js";
7
- import { readDirectory } from "./readDirectory.js";
8
- import { copyEntry } from "./copyEntry.js";
9
- import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js";
10
- import { statsToType } from "./internal/statsToType.js";
4
+ import { assertAndNormalizeDirectoryUrl } from "../path_and_url/directory_url_validation.js";
5
+ import { urlTargetsSameFileSystemPath } from "../path_and_url/url_targets_same_file_system_path.js";
6
+ import { statsToType } from "../read_write/stat/stats_to_type.js";
7
+ import { readEntryStat } from "../read_write/stat/read_entry_stat.js";
8
+ import { readSymbolicLink } from "../read_write/read_symbolic_link.js";
9
+ import { readDirectory } from "../read_write/read_directory.js";
10
+ import { copyEntry } from "./copy_entry.js";
11
11
 
12
12
  export const copyDirectoryContent = async ({
13
13
  signal = new AbortController().signal,
@@ -0,0 +1,90 @@
1
+ import { Abort } from "@jsenv/abort";
2
+ import { urlToFileSystemPath, resolveUrl } from "@jsenv/urls";
3
+
4
+ import { assertAndNormalizeDirectoryUrl } from "../path_and_url/directory_url_validation.js";
5
+ import { urlTargetsSameFileSystemPath } from "../path_and_url/url_targets_same_file_system_path.js";
6
+ import { statsToType } from "../read_write/stat/stats_to_type.js";
7
+ import { readEntryStatSync } from "../read_write/stat/read_entry_stat_sync.js";
8
+ import { readSymbolicLinkSync } from "../read_write/read_symbolic_link_sync.js";
9
+ import { readDirectorySync } from "../read_write/read_directory_sync.js";
10
+ import { copyEntrySync } from "./copy_entry_sync.js";
11
+
12
+ export const copyDirectoryContentSync = ({
13
+ signal = new AbortController().signal,
14
+ from,
15
+ to,
16
+ overwrite,
17
+ followLink = true,
18
+ } = {}) => {
19
+ const fromUrl = assertAndNormalizeDirectoryUrl(from);
20
+ const fromPath = urlToFileSystemPath(fromUrl);
21
+ let toUrl = assertAndNormalizeDirectoryUrl(to);
22
+ let toPath = urlToFileSystemPath(toUrl);
23
+
24
+ const sourceStats = readEntryStatSync(fromUrl, {
25
+ nullIfNotFound: true,
26
+ followLink: false,
27
+ });
28
+ if (!sourceStats) {
29
+ throw new Error(`no directory to copy content from at ${fromPath}`);
30
+ }
31
+ if (!sourceStats.isDirectory()) {
32
+ const sourceType = statsToType(sourceStats);
33
+ throw new Error(
34
+ `found a ${sourceType} instead of a directory at ${fromPath}`,
35
+ );
36
+ }
37
+
38
+ let destinationStats = readEntryStatSync(toUrl, {
39
+ nullIfNotFound: true,
40
+ // we force false here but in fact we will follow the destination link
41
+ // to know where we will actually move and detect useless move overrite etc..
42
+ followLink: false,
43
+ });
44
+ if (followLink && destinationStats && destinationStats.isSymbolicLink()) {
45
+ const linkTarget = readSymbolicLinkSync(toUrl);
46
+ toUrl = resolveUrl(linkTarget, toUrl);
47
+ toPath = urlToFileSystemPath(toUrl);
48
+ destinationStats = readEntryStatSync(toUrl, {
49
+ nullIfNotFound: true,
50
+ });
51
+ }
52
+
53
+ if (destinationStats === null) {
54
+ throw new Error(`no directory to copy content into at ${toPath}`);
55
+ }
56
+ if (!destinationStats.isDirectory()) {
57
+ const destinationType = statsToType(destinationStats);
58
+ throw new Error(
59
+ `destination leads to a ${destinationType} instead of a directory at ${toPath}`,
60
+ );
61
+ }
62
+
63
+ if (urlTargetsSameFileSystemPath(fromUrl, toUrl)) {
64
+ throw new Error(
65
+ `cannot copy directory content, source and destination are the same (${fromPath})`,
66
+ );
67
+ }
68
+
69
+ const copyOperation = Abort.startOperation();
70
+ copyOperation.addAbortSignal(signal);
71
+ try {
72
+ copyOperation.throwIfAborted();
73
+ const directoryEntries = readDirectorySync(fromUrl);
74
+ directoryEntries.forEach((directoryEntry) => {
75
+ const from = resolveUrl(directoryEntry, fromUrl);
76
+ const to = resolveUrl(directoryEntry, toUrl);
77
+ copyOperation.withSignal((signal) => {
78
+ copyEntrySync({
79
+ signal,
80
+ from,
81
+ to,
82
+ overwrite,
83
+ followLink,
84
+ });
85
+ });
86
+ });
87
+ } finally {
88
+ copyOperation.end();
89
+ }
90
+ };
@@ -0,0 +1,9 @@
1
+ import { copyEntrySync } from "./copy_entry_sync.js";
2
+
3
+ export const copyDirectorySync = ({ from, to, ...options }) => {
4
+ copyEntrySync({
5
+ from,
6
+ to,
7
+ ...options,
8
+ });
9
+ };
@@ -8,19 +8,19 @@ import {
8
8
  urlToFileSystemPath,
9
9
  } from "@jsenv/urls";
10
10
 
11
- import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js";
12
- import { statsToType } from "./internal/statsToType.js";
13
- import { binaryFlagsToPermissions } from "./internal/permissions.js";
14
- import { assertAndNormalizeFileUrl } from "./file_url_validation.js";
15
- import { writeDirectory } from "./writeDirectory.js";
16
- import { readEntryStat } from "./readEntryStat.js";
17
- import { ensureParentDirectories } from "./ensureParentDirectories.js";
18
- import { writeEntryPermissions } from "./writeEntryPermissions.js";
19
- import { writeEntryModificationTime } from "./writeEntryModificationTime.js";
20
- import { readDirectory } from "./readDirectory.js";
21
- import { readSymbolicLink } from "./readSymbolicLink.js";
22
- import { writeSymbolicLink } from "./writeSymbolicLink.js";
23
- import { removeEntry } from "./removeEntry.js";
11
+ import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
12
+ import { urlTargetsSameFileSystemPath } from "../path_and_url/url_targets_same_file_system_path.js";
13
+ import { readEntryStat } from "../read_write/stat/read_entry_stat.js";
14
+ import { statsToType } from "../read_write/stat/stats_to_type.js";
15
+ import { binaryFlagsToPermissions } from "../read_write/stat/permissions.js";
16
+ import { writeEntryPermissions } from "../read_write/stat/write_entry_permissions.js";
17
+ import { writeEntryModificationTime } from "../read_write/stat/write_entry_modification_time.js";
18
+ import { writeDirectory } from "../read_write/write_directory.js";
19
+ import { ensureParentDirectories } from "../read_write/ensure_parent_directories.js";
20
+ import { readDirectory } from "../read_write/read_directory.js";
21
+ import { readSymbolicLink } from "../read_write/read_symbolic_link.js";
22
+ import { writeSymbolicLink } from "../read_write/write_symbolic_link.js";
23
+ import { removeEntry } from "../remove/remove_entry.js";
24
24
 
25
25
  export const copyEntry = async ({
26
26
  signal = new AbortController().signal,
@@ -0,0 +1,212 @@
1
+ import { copyFileSync as copyFileSyncNode } from "node:fs";
2
+ import { Abort } from "@jsenv/abort";
3
+ import {
4
+ resolveUrl,
5
+ urlToRelativeUrl,
6
+ ensurePathnameTrailingSlash,
7
+ urlIsInsideOf,
8
+ urlToFileSystemPath,
9
+ } from "@jsenv/urls";
10
+
11
+ import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
12
+ import { urlTargetsSameFileSystemPath } from "../path_and_url/url_targets_same_file_system_path.js";
13
+ import { readEntryStatSync } from "../read_write/stat/read_entry_stat_sync.js";
14
+ import { statsToType } from "../read_write/stat/stats_to_type.js";
15
+ import { binaryFlagsToPermissions } from "../read_write/stat/permissions.js";
16
+ import { writeEntryPermissionsSync } from "../read_write/stat/write_entry_permissions_sync.js";
17
+ import { writeEntryModificationTimeSync } from "../read_write/stat/write_entry_modification_time_sync.js";
18
+ import { writeDirectorySync } from "../read_write/write_directory_sync.js";
19
+ import { ensureParentDirectoriesSync } from "../read_write/ensure_parent_directories_sync.js";
20
+ import { readDirectorySync } from "../read_write/read_directory_sync.js";
21
+ import { readSymbolicLinkSync } from "../read_write/read_symbolic_link_sync.js";
22
+ import { writeSymbolicLinkSync } from "../read_write/write_symbolic_link_sync.js";
23
+ import { removeEntrySync } from "../remove/remove_entry_sync.js";
24
+
25
+ export const copyEntrySync = ({
26
+ signal = new AbortController().signal,
27
+ from,
28
+ to,
29
+ overwrite = false,
30
+ preserveStat = true,
31
+ preserveMtime = preserveStat,
32
+ preservePermissions = preserveStat,
33
+ allowUseless = false,
34
+ followLink = true,
35
+ }) => {
36
+ const fromUrl = assertAndNormalizeFileUrl(from);
37
+ const fromPath = urlToFileSystemPath(fromUrl);
38
+ let toUrl = assertAndNormalizeFileUrl(to);
39
+ let toPath = urlToFileSystemPath(toUrl);
40
+
41
+ const sourceStats = readEntryStatSync(fromUrl, {
42
+ nullIfNotFound: true,
43
+ followLink: false,
44
+ });
45
+ if (!sourceStats) {
46
+ throw new Error(`nothing to copy at ${fromPath}`);
47
+ }
48
+
49
+ let destinationStats = readEntryStatSync(toUrl, {
50
+ nullIfNotFound: true,
51
+ // we force false here but in fact we will follow the destination link
52
+ // to know where we will actually move and detect useless move overrite etc..
53
+ followLink: false,
54
+ });
55
+
56
+ if (followLink && destinationStats && destinationStats.isSymbolicLink()) {
57
+ const linkTarget = readSymbolicLinkSync(toUrl);
58
+ toUrl = resolveUrl(linkTarget, toUrl);
59
+ toPath = urlToFileSystemPath(toUrl);
60
+ destinationStats = readEntryStatSync(toUrl, {
61
+ nullIfNotFound: true,
62
+ });
63
+ }
64
+
65
+ if (urlTargetsSameFileSystemPath(fromUrl, toUrl)) {
66
+ if (allowUseless) {
67
+ return;
68
+ }
69
+ throw new Error(
70
+ `cannot copy ${fromPath} because destination and source are the same`,
71
+ );
72
+ }
73
+
74
+ if (sourceStats.isDirectory()) {
75
+ toUrl = ensurePathnameTrailingSlash(toUrl);
76
+ }
77
+
78
+ const copyOperation = Abort.startOperation();
79
+ copyOperation.addAbortSignal(signal);
80
+
81
+ const visit = async (url, stats) => {
82
+ copyOperation.throwIfAborted();
83
+ if (stats.isFile() || stats.isCharacterDevice() || stats.isBlockDevice()) {
84
+ visitFile(url, stats);
85
+ } else if (stats.isSymbolicLink()) {
86
+ visitSymbolicLink(url, stats);
87
+ } else if (stats.isDirectory()) {
88
+ visitDirectory(ensurePathnameTrailingSlash(url), stats);
89
+ }
90
+ };
91
+
92
+ const visitFile = (fileUrl, fileStats) => {
93
+ const fileRelativeUrl = urlToRelativeUrl(fileUrl, fromUrl);
94
+ const fileCopyUrl = resolveUrl(fileRelativeUrl, toUrl);
95
+
96
+ copyFileSyncNode(
97
+ urlToFileSystemPath(fileUrl),
98
+ urlToFileSystemPath(fileCopyUrl),
99
+ );
100
+ copyStatsSync(fileCopyUrl, fileStats);
101
+ };
102
+
103
+ const visitSymbolicLink = (symbolicLinkUrl) => {
104
+ const symbolicLinkRelativeUrl = urlToRelativeUrl(symbolicLinkUrl, fromUrl);
105
+ const symbolicLinkTarget = readSymbolicLinkSync(symbolicLinkUrl);
106
+ const symbolicLinkTargetUrl = resolveUrl(
107
+ symbolicLinkTarget,
108
+ symbolicLinkUrl,
109
+ );
110
+ const linkIsRelative =
111
+ symbolicLinkTarget.startsWith("./") ||
112
+ symbolicLinkTarget.startsWith("../");
113
+
114
+ let symbolicLinkCopyTarget;
115
+ if (symbolicLinkTargetUrl === fromUrl) {
116
+ symbolicLinkCopyTarget = linkIsRelative ? symbolicLinkTarget : toUrl;
117
+ } else if (urlIsInsideOf(symbolicLinkTargetUrl, fromUrl)) {
118
+ // symbolic link targets something inside the directory we want to copy
119
+ // reflects it inside the copied directory structure
120
+ const linkCopyTargetRelative = urlToRelativeUrl(
121
+ symbolicLinkTargetUrl,
122
+ fromUrl,
123
+ );
124
+ symbolicLinkCopyTarget = linkIsRelative
125
+ ? `./${linkCopyTargetRelative}`
126
+ : resolveUrl(linkCopyTargetRelative, toUrl);
127
+ } else {
128
+ // symbolic link targets something outside the directory we want to copy
129
+ symbolicLinkCopyTarget = symbolicLinkTarget;
130
+ }
131
+
132
+ // we must guess ourself the type of the symlink
133
+ // because the destination might not exists because not yet copied
134
+ // https://nodejs.org/dist/latest-v13.x/docs/api/fs.html#fs_fs_symlink_target_path_type_callback
135
+ const targetStats = readEntryStatSync(symbolicLinkTargetUrl, {
136
+ nullIfNotFound: true,
137
+ followLink: false,
138
+ });
139
+ const linkType = targetStats && targetStats.isDirectory() ? "dir" : "file";
140
+
141
+ const symbolicLinkCopyUrl = resolveUrl(symbolicLinkRelativeUrl, toUrl);
142
+ writeSymbolicLinkSync({
143
+ from: symbolicLinkCopyUrl,
144
+ to: symbolicLinkCopyTarget,
145
+ type: linkType,
146
+ });
147
+ };
148
+
149
+ const copyStatsSync = (toUrl, stats) => {
150
+ if (preservePermissions || preserveMtime) {
151
+ const { mode, mtimeMs } = stats;
152
+ if (preservePermissions) {
153
+ writeEntryPermissionsSync(toUrl, binaryFlagsToPermissions(mode));
154
+ }
155
+ if (preserveMtime) {
156
+ writeEntryModificationTimeSync(toUrl, mtimeMs);
157
+ }
158
+ }
159
+ };
160
+
161
+ const visitDirectory = async (directoryUrl, directoryStats) => {
162
+ const directoryRelativeUrl = urlToRelativeUrl(directoryUrl, fromUrl);
163
+ const directoryCopyUrl = resolveUrl(directoryRelativeUrl, toUrl);
164
+
165
+ writeDirectorySync(directoryCopyUrl);
166
+ copyDirectoryContentSync(directoryUrl);
167
+ copyStatsSync(directoryCopyUrl, directoryStats);
168
+ };
169
+
170
+ const copyDirectoryContentSync = async (directoryUrl) => {
171
+ const names = readDirectorySync(directoryUrl);
172
+ names.forEach((name) => {
173
+ const entryUrl = resolveUrl(name, directoryUrl);
174
+ const stats = readEntryStatSync(entryUrl, {
175
+ followLink: false,
176
+ });
177
+ visit(entryUrl, stats);
178
+ });
179
+ };
180
+
181
+ try {
182
+ if (destinationStats) {
183
+ const sourceType = statsToType(sourceStats);
184
+ const destinationType = statsToType(destinationStats);
185
+
186
+ if (sourceType !== destinationType) {
187
+ throw new Error(
188
+ `cannot copy ${sourceType} from ${fromPath} to ${toPath} because destination exists and is not a ${sourceType} (it's a ${destinationType})`,
189
+ );
190
+ }
191
+ if (!overwrite) {
192
+ throw new Error(
193
+ `cannot copy ${sourceType} from ${fromPath} to ${toPath} because destination exists and overwrite option is disabled`,
194
+ );
195
+ }
196
+
197
+ // remove file, link, directory...
198
+ removeEntrySync(toUrl, {
199
+ signal: copyOperation.signal,
200
+ recursive: true,
201
+ allowUseless: true,
202
+ });
203
+ } else {
204
+ ensureParentDirectoriesSync(toUrl);
205
+ }
206
+
207
+ copyOperation.throwIfAborted();
208
+ visit(fromUrl, sourceStats);
209
+ } finally {
210
+ copyOperation.end();
211
+ }
212
+ };
@@ -2,13 +2,14 @@ import { readdirSync, statSync } from "node:fs";
2
2
  import { URL_META } from "@jsenv/url-meta";
3
3
  import { urlToFileSystemPath, urlToRelativeUrl } from "@jsenv/urls";
4
4
 
5
- import { assertAndNormalizeDirectoryUrl } from "./directory_url_validation.js";
6
- import { statsToType } from "./internal/statsToType.js";
7
- import { guardTooFastSecondCallPerFile } from "./internal/guard_second_call.js";
8
- import { createWatcher } from "./internal/createWatcher.js";
9
- import { trackResources } from "./internal/track_resources.js";
5
+ import { assertAndNormalizeDirectoryUrl } from "../path_and_url/directory_url_validation.js";
6
+ import { statsToType } from "../read_write/stat/stats_to_type.js";
7
+ import { guardTooFastSecondCallPerFile } from "./guard_second_call.js";
8
+ import { createWatcher } from "./create_watcher.js";
9
+ import { trackResources } from "./track_resources.js";
10
10
 
11
- const fsWatchSupportsRecursive = process.platform !== "linux";
11
+ const isLinux = process.platform === "linux";
12
+ const fsWatchSupportsRecursive = !isLinux;
12
13
 
13
14
  export const registerDirectoryLifecycle = (
14
15
  source,
@@ -344,10 +345,10 @@ const shouldCallUpdated = (entryInfo) => {
344
345
  if (stat.atimeMs < stat.mtimeMs) {
345
346
  return true;
346
347
  }
347
- if (stat.mtimeMs > previousInfo.stat.mtimeMs) {
348
- return true;
348
+ if (isLinux && stat.mtimeMs <= previousInfo.stat.mtimeMs) {
349
+ return false;
349
350
  }
350
- return false;
351
+ return true;
351
352
  };
352
353
 
353
354
  const undefinedOrFunction = (value) => {
@@ -2,11 +2,11 @@ import { statSync } from "node:fs";
2
2
  import { dirname, basename } from "node:path";
3
3
  import { urlToFileSystemPath } from "@jsenv/urls";
4
4
 
5
- import { assertAndNormalizeFileUrl } from "./file_url_validation.js";
6
- import { guardTooFastSecondCall } from "./internal/guard_second_call.js";
7
- import { statsToType } from "./internal/statsToType.js";
8
- import { createWatcher } from "./internal/createWatcher.js";
9
- import { trackResources } from "./internal/track_resources.js";
5
+ import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
6
+ import { statsToType } from "../read_write/stat/stats_to_type.js";
7
+ import { trackResources } from "./track_resources.js";
8
+ import { createWatcher } from "./create_watcher.js";
9
+ import { guardTooFastSecondCall } from "./guard_second_call.js";
10
10
 
11
11
  export const registerFileLifecycle = (
12
12
  source,
@@ -1,11 +1,11 @@
1
1
  import { Abort } from "@jsenv/abort";
2
-
3
2
  import { URL_META } from "@jsenv/url-meta";
4
3
  import { urlToRelativeUrl } from "@jsenv/urls";
5
- import { assertAndNormalizeDirectoryUrl } from "./directory_url_validation.js";
6
- import { readDirectory } from "./readDirectory.js";
7
- import { readEntryStat } from "./readEntryStat.js";
8
- import { comparePathnames } from "./comparePathnames.js";
4
+
5
+ import { assertAndNormalizeDirectoryUrl } from "../path_and_url/directory_url_validation.js";
6
+ import { comparePathnames } from "../path_and_url/compare_pathnames.js";
7
+ import { readDirectory } from "../read_write/read_directory.js";
8
+ import { readEntryStat } from "../read_write/stat/read_entry_stat.js";
9
9
 
10
10
  export const collectDirectoryMatchReport = async ({
11
11
  signal = new AbortController().signal,
@@ -2,10 +2,10 @@ import { Abort } from "@jsenv/abort";
2
2
  import { URL_META } from "@jsenv/url-meta";
3
3
  import { urlToRelativeUrl } from "@jsenv/urls";
4
4
 
5
- import { assertAndNormalizeDirectoryUrl } from "./directory_url_validation.js";
6
- import { readDirectory } from "./readDirectory.js";
7
- import { readEntryStat } from "./readEntryStat.js";
8
- import { comparePathnames } from "./comparePathnames.js";
5
+ import { assertAndNormalizeDirectoryUrl } from "../path_and_url/directory_url_validation.js";
6
+ import { comparePathnames } from "../path_and_url/compare_pathnames.js";
7
+ import { readEntryStat } from "../read_write/stat/read_entry_stat.js";
8
+ import { readDirectory } from "../read_write/read_directory.js";
9
9
 
10
10
  export const collectFiles = async ({
11
11
  signal = new AbortController().signal,
@@ -1,4 +1,4 @@
1
- import { collectFiles } from "./collectFiles.js";
1
+ import { collectFiles } from "./collect_files.js";
2
2
 
3
3
  export const listFilesMatching = async ({
4
4
  directoryUrl = process.cwd(),
package/src/main.js CHANGED
@@ -1,41 +1,84 @@
1
+ // we might wanna expose the sync versions as follows:
2
+ // readDirectory.sync()
3
+ // +1: very easy to switch between sync/async versions of a given function
4
+ // -1: code splitting
5
+ // -1: unusual
6
+
7
+ // path_and_url
1
8
  export {
2
9
  validateDirectoryUrl,
3
10
  assertAndNormalizeDirectoryUrl,
4
- } from "./directory_url_validation.js";
11
+ } from "./path_and_url/directory_url_validation.js";
5
12
  export {
6
13
  validateFileUrl,
7
14
  assertAndNormalizeFileUrl,
8
- } from "./file_url_validation.js";
9
- export { assertDirectoryPresence } from "./assertDirectoryPresence.js";
10
- export { assertFilePresence } from "./assertFilePresence.js";
11
- export { bufferToEtag } from "./bufferToEtag.js";
12
- export { collectDirectoryMatchReport } from "./collectDirectoryMatchReport.js";
13
- export { collectFiles } from "./collectFiles.js";
14
- export { comparePathnames } from "./comparePathnames.js";
15
- export { ensureEmptyDirectory } from "./ensureEmptyDirectory.js";
16
- export { ensureParentDirectories } from "./ensureParentDirectories.js";
17
- export { ensureWindowsDriveLetter } from "./ensureWindowsDriveLetter.js";
18
- export { copyEntry } from "./copyEntry.js";
19
- export { copyDirectoryContent } from "./copyDirectoryContent.js";
20
- export { getRealFileSystemUrlSync } from "./getRealFileSystemUrlSync.js";
21
- export { grantPermissionsOnEntry } from "./grantPermissionsOnEntry.js";
22
- export { listFilesMatching } from "./listFilesMatching.js";
23
- export { moveDirectoryContent } from "./moveDirectoryContent.js";
24
- export { moveEntry } from "./moveEntry.js";
25
- export { readDirectory } from "./readDirectory.js";
26
- export { readFile } from "./readFile.js";
27
- export { readFileSync } from "./readFileSync.js";
28
- export { readEntryModificationTime } from "./readEntryModificationTime.js";
29
- export { readEntryPermissions } from "./readEntryPermissions.js";
30
- export { readEntryStat } from "./readEntryStat.js";
31
- export { readSymbolicLink } from "./readSymbolicLink.js";
32
- export { registerDirectoryLifecycle } from "./registerDirectoryLifecycle.js";
33
- export { registerFileLifecycle } from "./registerFileLifecycle.js";
34
- export { removeEntry } from "./removeEntry.js";
35
- export { testEntryPermissions } from "./testEntryPermissions.js";
36
- export { writeDirectory } from "./writeDirectory.js";
37
- export { writeFile } from "./writeFile.js";
38
- export { writeFileSync } from "./writeFileSync.js";
39
- export { writeEntryModificationTime } from "./writeEntryModificationTime.js";
40
- export { writeEntryPermissions } from "./writeEntryPermissions.js";
41
- export { writeSymbolicLink } from "./writeSymbolicLink.js";
15
+ } from "./path_and_url/file_url_validation.js";
16
+ export { comparePathnames } from "./path_and_url/compare_pathnames.js";
17
+ export { ensureWindowsDriveLetter } from "./path_and_url/ensure_windows_drive_letter.js";
18
+
19
+ // list
20
+ export { collectDirectoryMatchReport } from "./list/collect_directory_match_report.js";
21
+ export { collectFiles } from "./list/collect_files.js";
22
+ export { listFilesMatching } from "./list/list_files_matching.js";
23
+
24
+ // read and write
25
+ export { ensureParentDirectories } from "./read_write/ensure_parent_directories.js";
26
+ export { readDirectory } from "./read_write/read_directory.js";
27
+ export { writeDirectory } from "./read_write/write_directory.js";
28
+ export { readFile } from "./read_write/read_file.js";
29
+ export { writeFile } from "./read_write/write_file.js";
30
+ export { readFileSync } from "./read_write/read_file_sync.js";
31
+ export { writeFileSync } from "./read_write/write_file_sync.js";
32
+ export { readSymbolicLink } from "./read_write/read_symbolic_link.js";
33
+ export { writeSymbolicLink } from "./read_write/write_symbolic_link.js";
34
+ // read and write sync
35
+ export { ensureParentDirectoriesSync } from "./read_write/ensure_parent_directories_sync.js";
36
+ export { readDirectorySync } from "./read_write/read_directory_sync.js";
37
+ export { getRealFileSystemUrlSync } from "./read_write/get_real_file_system_url_sync.js";
38
+ export { readSymbolicLinkSync } from "./read_write/read_symbolic_link_sync.js";
39
+ export { writeDirectorySync } from "./read_write/write_directory_sync.js";
40
+ export { readFileStructureSync } from "./read_write/read_file_structure_sync.js";
41
+ export { writeFileStructureSync } from "./read_write/write_file_structure_sync.js";
42
+
43
+ // stat
44
+ export { readEntryStat } from "./read_write/stat/read_entry_stat.js";
45
+ export { readEntryModificationTime } from "./read_write/stat/read_entry_modification_time.js";
46
+ export { writeEntryModificationTime } from "./read_write/stat/write_entry_modification_time.js";
47
+ export { readEntryPermissions } from "./read_write/stat/read_entry_permissions.js";
48
+ export { writeEntryPermissions } from "./read_write/stat/write_entry_permissions.js";
49
+ export { grantPermissionsOnEntry } from "./read_write/stat/grant_permissions_on_entry.js";
50
+ export { testEntryPermissions } from "./read_write/stat/test_entry_permissions.js";
51
+ // stat sync
52
+ export { readEntryStatSync } from "./read_write/stat/read_entry_stat_sync.js";
53
+ export { writeEntryModificationTimeSync } from "./read_write/stat/write_entry_modification_time_sync.js";
54
+
55
+ // move
56
+ export { moveDirectoryContent } from "./move/move_directory_content.js";
57
+ export { moveEntry } from "./move/move_entry.js";
58
+
59
+ // copy
60
+ export { copyEntry } from "./copy/copy_entry.js";
61
+ export { copyDirectoryContent } from "./copy/copy_directory_content.js";
62
+ export { copyEntrySync } from "./copy/copy_entry_sync.js";
63
+ export { copyDirectorySync } from "./copy/copy_directory_sync.js";
64
+ export { copyDirectoryContentSync } from "./copy/copy_directory_content_sync.js";
65
+
66
+ // remove
67
+ export { ensureEmptyDirectory } from "./remove/ensure_empty_directory.js";
68
+ export { ensureEmptyDirectorySync } from "./remove/ensure_empty_directory_sync.js";
69
+ export { clearDirectorySync } from "./remove/clear_directory_sync.js";
70
+ export { removeDirectory } from "./remove/remove_directory.js";
71
+ export { removeFile } from "./remove/remove_file.js";
72
+ export { removeEntry } from "./remove/remove_entry.js";
73
+ export { removeDirectorySync } from "./remove/remove_directory_sync.js";
74
+ export { removeFileSync } from "./remove/remove_file_sync.js";
75
+ export { removeEntrySync } from "./remove/remove_entry_sync.js";
76
+
77
+ // lifecycle
78
+ export { registerDirectoryLifecycle } from "./lifecycle/register_directory_lifecycle.js";
79
+ export { registerFileLifecycle } from "./lifecycle/register_file_lifecycle.js";
80
+
81
+ // other
82
+ export { assertDirectoryPresence } from "./assert_directory_presence.js";
83
+ export { assertFilePresence } from "./assert_file_presence.js";
84
+ export { bufferToEtag } from "./buffer_to_etag.js";