@jsenv/filesystem 4.2.3 → 4.2.4
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/README.md +17 -17
- package/package.json +4 -4
- package/src/assertDirectoryPresence.js +10 -10
- package/src/assertFilePresence.js +10 -10
- package/src/bufferToEtag.js +11 -11
- package/src/collectDirectoryMatchReport.js +38 -38
- package/src/collectFiles.js +35 -35
- package/src/comparePathnames.js +23 -23
- package/src/copyDirectoryContent.js +37 -37
- package/src/copyEntry.js +88 -88
- package/src/directory_url_validation.js +14 -14
- package/src/ensureEmptyDirectory.js +13 -13
- package/src/ensureParentDirectories.js +9 -9
- package/src/ensureWindowsDriveLetter.js +19 -19
- package/src/fileSystemRootUrl.js +1 -1
- package/src/file_url_validation.js +14 -14
- package/src/getRealFileSystemUrlSync.js +24 -24
- package/src/grantPermissionsOnEntry.js +9 -9
- package/src/internal/createWatcher.js +12 -12
- package/src/internal/getCommandArgument.js +7 -7
- package/src/internal/guard_second_call.js +20 -20
- package/src/internal/permissions.js +26 -26
- package/src/internal/read_entry_info.js +8 -8
- package/src/internal/statsToType.js +9 -9
- package/src/internal/track_resources.js +12 -12
- package/src/internal/urlTargetsSameFileSystemPath.js +4 -4
- package/src/listFilesMatching.js +5 -5
- package/src/main.js +35 -35
- package/src/moveDirectoryContent.js +37 -37
- package/src/moveEntry.js +44 -44
- package/src/readDirectory.js +21 -21
- package/src/readEntryModificationTime.js +4 -4
- package/src/readEntryPermissions.js +10 -10
- package/src/readEntryStat.js +27 -27
- package/src/readFile.js +12 -12
- package/src/readFileSync.js +9 -9
- package/src/readSymbolicLink.js +9 -9
- package/src/registerDirectoryLifecycle.js +118 -118
- package/src/registerFileLifecycle.js +66 -66
- package/src/removeEntry.js +81 -81
- package/src/testEntryPermissions.js +15 -15
- package/src/writeDirectory.js +17 -17
- package/src/writeEntryModificationTime.js +10 -10
- package/src/writeEntryPermissions.js +26 -26
- package/src/writeFile.js +16 -16
- package/src/writeFileSync.js +10 -10
- package/src/writeSymbolicLink.js +39 -39
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { statSync } from "node:fs"
|
|
2
|
-
import { dirname, basename } from "node:path"
|
|
3
|
-
import { urlToFileSystemPath } from "@jsenv/urls"
|
|
1
|
+
import { statSync } from "node:fs";
|
|
2
|
+
import { dirname, basename } from "node:path";
|
|
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 "./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";
|
|
10
10
|
|
|
11
11
|
export const registerFileLifecycle = (
|
|
12
12
|
source,
|
|
@@ -19,138 +19,138 @@ export const registerFileLifecycle = (
|
|
|
19
19
|
cooldownBetweenFileEvents = 0,
|
|
20
20
|
},
|
|
21
21
|
) => {
|
|
22
|
-
const sourceUrl = assertAndNormalizeFileUrl(source)
|
|
22
|
+
const sourceUrl = assertAndNormalizeFileUrl(source);
|
|
23
23
|
if (!undefinedOrFunction(added)) {
|
|
24
|
-
throw new TypeError(`added must be a function or undefined, got ${added}`)
|
|
24
|
+
throw new TypeError(`added must be a function or undefined, got ${added}`);
|
|
25
25
|
}
|
|
26
26
|
if (!undefinedOrFunction(updated)) {
|
|
27
27
|
throw new TypeError(
|
|
28
28
|
`updated must be a function or undefined, got ${updated}`,
|
|
29
|
-
)
|
|
29
|
+
);
|
|
30
30
|
}
|
|
31
31
|
if (!undefinedOrFunction(removed)) {
|
|
32
32
|
throw new TypeError(
|
|
33
33
|
`removed must be a function or undefined, got ${removed}`,
|
|
34
|
-
)
|
|
34
|
+
);
|
|
35
35
|
}
|
|
36
36
|
if (cooldownBetweenFileEvents) {
|
|
37
37
|
if (added) {
|
|
38
|
-
added = guardTooFastSecondCall(added, cooldownBetweenFileEvents)
|
|
38
|
+
added = guardTooFastSecondCall(added, cooldownBetweenFileEvents);
|
|
39
39
|
}
|
|
40
40
|
if (updated) {
|
|
41
|
-
updated = guardTooFastSecondCall(updated, cooldownBetweenFileEvents)
|
|
41
|
+
updated = guardTooFastSecondCall(updated, cooldownBetweenFileEvents);
|
|
42
42
|
}
|
|
43
43
|
if (removed) {
|
|
44
|
-
removed = guardTooFastSecondCall(removed, cooldownBetweenFileEvents)
|
|
44
|
+
removed = guardTooFastSecondCall(removed, cooldownBetweenFileEvents);
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const tracker = trackResources()
|
|
48
|
+
const tracker = trackResources();
|
|
49
49
|
|
|
50
50
|
const handleFileFound = ({ existent }) => {
|
|
51
51
|
const fileMutationStopWatching = watchFileMutation(sourceUrl, {
|
|
52
52
|
updated,
|
|
53
53
|
removed: () => {
|
|
54
|
-
fileMutationStopTracking()
|
|
55
|
-
watchFileAdded()
|
|
54
|
+
fileMutationStopTracking();
|
|
55
|
+
watchFileAdded();
|
|
56
56
|
if (removed) {
|
|
57
|
-
removed()
|
|
57
|
+
removed();
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
keepProcessAlive,
|
|
61
|
-
})
|
|
61
|
+
});
|
|
62
62
|
const fileMutationStopTracking = tracker.registerCleanupCallback(
|
|
63
63
|
fileMutationStopWatching,
|
|
64
|
-
)
|
|
64
|
+
);
|
|
65
65
|
|
|
66
66
|
if (added) {
|
|
67
67
|
if (existent) {
|
|
68
68
|
if (notifyExistent) {
|
|
69
|
-
added({ existent: true })
|
|
69
|
+
added({ existent: true });
|
|
70
70
|
}
|
|
71
71
|
} else {
|
|
72
|
-
added({})
|
|
72
|
+
added({});
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
}
|
|
75
|
+
};
|
|
76
76
|
|
|
77
77
|
const watchFileAdded = () => {
|
|
78
78
|
const fileCreationStopWatching = watchFileCreation(
|
|
79
79
|
sourceUrl,
|
|
80
80
|
() => {
|
|
81
|
-
fileCreationgStopTracking()
|
|
82
|
-
handleFileFound({ existent: false })
|
|
81
|
+
fileCreationgStopTracking();
|
|
82
|
+
handleFileFound({ existent: false });
|
|
83
83
|
},
|
|
84
84
|
keepProcessAlive,
|
|
85
|
-
)
|
|
85
|
+
);
|
|
86
86
|
const fileCreationgStopTracking = tracker.registerCleanupCallback(
|
|
87
87
|
fileCreationStopWatching,
|
|
88
|
-
)
|
|
89
|
-
}
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
90
|
|
|
91
|
-
const sourceType = entryToTypeOrNull(sourceUrl)
|
|
91
|
+
const sourceType = entryToTypeOrNull(sourceUrl);
|
|
92
92
|
if (sourceType === null) {
|
|
93
93
|
if (added) {
|
|
94
|
-
watchFileAdded()
|
|
94
|
+
watchFileAdded();
|
|
95
95
|
} else {
|
|
96
96
|
throw new Error(
|
|
97
97
|
`${urlToFileSystemPath(sourceUrl)} must lead to a file, found nothing`,
|
|
98
|
-
)
|
|
98
|
+
);
|
|
99
99
|
}
|
|
100
100
|
} else if (sourceType === "file") {
|
|
101
|
-
handleFileFound({ existent: true })
|
|
101
|
+
handleFileFound({ existent: true });
|
|
102
102
|
} else {
|
|
103
103
|
throw new Error(
|
|
104
104
|
`${urlToFileSystemPath(
|
|
105
105
|
sourceUrl,
|
|
106
106
|
)} must lead to a file, type found instead`,
|
|
107
|
-
)
|
|
107
|
+
);
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
return tracker.cleanup
|
|
111
|
-
}
|
|
110
|
+
return tracker.cleanup;
|
|
111
|
+
};
|
|
112
112
|
|
|
113
113
|
const entryToTypeOrNull = (url) => {
|
|
114
114
|
try {
|
|
115
|
-
const stats = statSync(new URL(url))
|
|
116
|
-
return statsToType(stats)
|
|
115
|
+
const stats = statSync(new URL(url));
|
|
116
|
+
return statsToType(stats);
|
|
117
117
|
} catch (e) {
|
|
118
118
|
if (e.code === "ENOENT") {
|
|
119
|
-
return null
|
|
119
|
+
return null;
|
|
120
120
|
}
|
|
121
|
-
throw e
|
|
121
|
+
throw e;
|
|
122
122
|
}
|
|
123
|
-
}
|
|
123
|
+
};
|
|
124
124
|
|
|
125
125
|
const undefinedOrFunction = (value) =>
|
|
126
|
-
typeof value === "undefined" || typeof value === "function"
|
|
126
|
+
typeof value === "undefined" || typeof value === "function";
|
|
127
127
|
|
|
128
128
|
const watchFileCreation = (source, callback, keepProcessAlive) => {
|
|
129
|
-
const sourcePath = urlToFileSystemPath(source)
|
|
130
|
-
const sourceFilename = basename(sourcePath)
|
|
131
|
-
const directoryPath = dirname(sourcePath)
|
|
129
|
+
const sourcePath = urlToFileSystemPath(source);
|
|
130
|
+
const sourceFilename = basename(sourcePath);
|
|
131
|
+
const directoryPath = dirname(sourcePath);
|
|
132
132
|
let directoryWatcher = createWatcher(directoryPath, {
|
|
133
133
|
persistent: keepProcessAlive,
|
|
134
|
-
})
|
|
134
|
+
});
|
|
135
135
|
directoryWatcher.on("change", (eventType, filename) => {
|
|
136
|
-
if (filename && filename !== sourceFilename) return
|
|
136
|
+
if (filename && filename !== sourceFilename) return;
|
|
137
137
|
|
|
138
|
-
const type = entryToTypeOrNull(source)
|
|
138
|
+
const type = entryToTypeOrNull(source);
|
|
139
139
|
// ignore if something else with that name gets created
|
|
140
140
|
// we are only interested into files
|
|
141
|
-
if (type !== "file") return
|
|
141
|
+
if (type !== "file") return;
|
|
142
142
|
|
|
143
|
-
directoryWatcher.close()
|
|
144
|
-
directoryWatcher = undefined
|
|
145
|
-
callback()
|
|
146
|
-
})
|
|
143
|
+
directoryWatcher.close();
|
|
144
|
+
directoryWatcher = undefined;
|
|
145
|
+
callback();
|
|
146
|
+
});
|
|
147
147
|
|
|
148
148
|
return () => {
|
|
149
149
|
if (directoryWatcher) {
|
|
150
|
-
directoryWatcher.close()
|
|
150
|
+
directoryWatcher.close();
|
|
151
151
|
}
|
|
152
|
-
}
|
|
153
|
-
}
|
|
152
|
+
};
|
|
153
|
+
};
|
|
154
154
|
|
|
155
155
|
const watchFileMutation = (
|
|
156
156
|
sourceUrl,
|
|
@@ -158,27 +158,27 @@ const watchFileMutation = (
|
|
|
158
158
|
) => {
|
|
159
159
|
let watcher = createWatcher(urlToFileSystemPath(sourceUrl), {
|
|
160
160
|
persistent: keepProcessAlive,
|
|
161
|
-
})
|
|
161
|
+
});
|
|
162
162
|
|
|
163
163
|
watcher.on("change", () => {
|
|
164
|
-
const sourceType = entryToTypeOrNull(sourceUrl)
|
|
164
|
+
const sourceType = entryToTypeOrNull(sourceUrl);
|
|
165
165
|
|
|
166
166
|
if (sourceType === null) {
|
|
167
|
-
watcher.close()
|
|
168
|
-
watcher = undefined
|
|
167
|
+
watcher.close();
|
|
168
|
+
watcher = undefined;
|
|
169
169
|
if (removed) {
|
|
170
|
-
removed()
|
|
170
|
+
removed();
|
|
171
171
|
}
|
|
172
172
|
} else if (sourceType === "file") {
|
|
173
173
|
if (updated) {
|
|
174
|
-
updated()
|
|
174
|
+
updated();
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
|
-
})
|
|
177
|
+
});
|
|
178
178
|
|
|
179
179
|
return () => {
|
|
180
180
|
if (watcher) {
|
|
181
|
-
watcher.close()
|
|
181
|
+
watcher.close();
|
|
182
182
|
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
183
|
+
};
|
|
184
|
+
};
|
package/src/removeEntry.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { unlink, rmdir, openSync, closeSync } from "node:fs"
|
|
2
|
-
import { Abort } from "@jsenv/abort"
|
|
1
|
+
import { unlink, rmdir, openSync, closeSync } from "node:fs";
|
|
2
|
+
import { Abort } from "@jsenv/abort";
|
|
3
3
|
import {
|
|
4
4
|
ensurePathnameTrailingSlash,
|
|
5
5
|
urlToFileSystemPath,
|
|
6
6
|
resolveUrl,
|
|
7
|
-
} from "@jsenv/urls"
|
|
7
|
+
} from "@jsenv/urls";
|
|
8
8
|
|
|
9
|
-
import { assertAndNormalizeFileUrl } from "./file_url_validation.js"
|
|
10
|
-
import { readEntryStat } from "./readEntryStat.js"
|
|
11
|
-
import { readDirectory } from "./readDirectory.js"
|
|
9
|
+
import { assertAndNormalizeFileUrl } from "./file_url_validation.js";
|
|
10
|
+
import { readEntryStat } from "./readEntryStat.js";
|
|
11
|
+
import { readDirectory } from "./readDirectory.js";
|
|
12
12
|
|
|
13
13
|
export const removeEntry = async (
|
|
14
14
|
source,
|
|
@@ -21,22 +21,22 @@ export const removeEntry = async (
|
|
|
21
21
|
onlyContent = false,
|
|
22
22
|
} = {},
|
|
23
23
|
) => {
|
|
24
|
-
const sourceUrl = assertAndNormalizeFileUrl(source)
|
|
24
|
+
const sourceUrl = assertAndNormalizeFileUrl(source);
|
|
25
25
|
|
|
26
|
-
const removeOperation = Abort.startOperation()
|
|
27
|
-
removeOperation.addAbortSignal(signal)
|
|
26
|
+
const removeOperation = Abort.startOperation();
|
|
27
|
+
removeOperation.addAbortSignal(signal);
|
|
28
28
|
|
|
29
29
|
try {
|
|
30
|
-
removeOperation.throwIfAborted()
|
|
30
|
+
removeOperation.throwIfAborted();
|
|
31
31
|
const sourceStats = await readEntryStat(sourceUrl, {
|
|
32
32
|
nullIfNotFound: true,
|
|
33
33
|
followLink: false,
|
|
34
|
-
})
|
|
34
|
+
});
|
|
35
35
|
if (!sourceStats) {
|
|
36
36
|
if (allowUseless) {
|
|
37
|
-
return
|
|
37
|
+
return;
|
|
38
38
|
}
|
|
39
|
-
throw new Error(`nothing to remove at ${urlToFileSystemPath(sourceUrl)}`)
|
|
39
|
+
throw new Error(`nothing to remove at ${urlToFileSystemPath(sourceUrl)}`);
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
// https://nodejs.org/dist/latest-v13.x/docs/api/fs.html#fs_class_fs_stats
|
|
@@ -54,7 +54,7 @@ export const removeEntry = async (
|
|
|
54
54
|
maxRetries,
|
|
55
55
|
retryDelay,
|
|
56
56
|
},
|
|
57
|
-
)
|
|
57
|
+
);
|
|
58
58
|
} else if (sourceStats.isDirectory()) {
|
|
59
59
|
await removeDirectory(ensurePathnameTrailingSlash(sourceUrl), {
|
|
60
60
|
signal: removeOperation.signal,
|
|
@@ -62,42 +62,42 @@ export const removeEntry = async (
|
|
|
62
62
|
maxRetries,
|
|
63
63
|
retryDelay,
|
|
64
64
|
onlyContent,
|
|
65
|
-
})
|
|
65
|
+
});
|
|
66
66
|
}
|
|
67
67
|
} finally {
|
|
68
|
-
await removeOperation.end()
|
|
68
|
+
await removeOperation.end();
|
|
69
69
|
}
|
|
70
|
-
}
|
|
70
|
+
};
|
|
71
71
|
|
|
72
72
|
const removeNonDirectory = (sourceUrl, { maxRetries, retryDelay }) => {
|
|
73
|
-
const sourcePath = urlToFileSystemPath(sourceUrl)
|
|
73
|
+
const sourcePath = urlToFileSystemPath(sourceUrl);
|
|
74
74
|
|
|
75
|
-
let retryCount = 0
|
|
75
|
+
let retryCount = 0;
|
|
76
76
|
const attempt = () => {
|
|
77
77
|
return unlinkNaive(sourcePath, {
|
|
78
78
|
...(retryCount >= maxRetries
|
|
79
79
|
? {}
|
|
80
80
|
: {
|
|
81
81
|
handleTemporaryError: async () => {
|
|
82
|
-
retryCount
|
|
82
|
+
retryCount++;
|
|
83
83
|
return new Promise((resolve) => {
|
|
84
84
|
setTimeout(() => {
|
|
85
|
-
resolve(attempt())
|
|
86
|
-
}, retryCount * retryDelay)
|
|
87
|
-
})
|
|
85
|
+
resolve(attempt());
|
|
86
|
+
}, retryCount * retryDelay);
|
|
87
|
+
});
|
|
88
88
|
},
|
|
89
89
|
}),
|
|
90
|
-
})
|
|
91
|
-
}
|
|
92
|
-
return attempt()
|
|
93
|
-
}
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
return attempt();
|
|
93
|
+
};
|
|
94
94
|
|
|
95
95
|
const unlinkNaive = (sourcePath, { handleTemporaryError = null } = {}) => {
|
|
96
96
|
return new Promise((resolve, reject) => {
|
|
97
97
|
unlink(sourcePath, (error) => {
|
|
98
98
|
if (error) {
|
|
99
99
|
if (error.code === "ENOENT") {
|
|
100
|
-
resolve()
|
|
100
|
+
resolve();
|
|
101
101
|
} else if (
|
|
102
102
|
handleTemporaryError &&
|
|
103
103
|
(error.code === "EBUSY" ||
|
|
@@ -105,34 +105,34 @@ const unlinkNaive = (sourcePath, { handleTemporaryError = null } = {}) => {
|
|
|
105
105
|
error.code === "ENFILE" ||
|
|
106
106
|
error.code === "ENOENT")
|
|
107
107
|
) {
|
|
108
|
-
resolve(handleTemporaryError(error))
|
|
108
|
+
resolve(handleTemporaryError(error));
|
|
109
109
|
} else {
|
|
110
|
-
reject(error)
|
|
110
|
+
reject(error);
|
|
111
111
|
}
|
|
112
112
|
} else {
|
|
113
|
-
resolve()
|
|
113
|
+
resolve();
|
|
114
114
|
}
|
|
115
|
-
})
|
|
116
|
-
})
|
|
117
|
-
}
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
};
|
|
118
118
|
|
|
119
119
|
const removeDirectory = async (
|
|
120
120
|
rootDirectoryUrl,
|
|
121
121
|
{ signal, maxRetries, retryDelay, recursive, onlyContent },
|
|
122
122
|
) => {
|
|
123
|
-
const removeDirectoryOperation = Abort.startOperation()
|
|
124
|
-
removeDirectoryOperation.addAbortSignal(signal)
|
|
123
|
+
const removeDirectoryOperation = Abort.startOperation();
|
|
124
|
+
removeDirectoryOperation.addAbortSignal(signal);
|
|
125
125
|
|
|
126
126
|
const visit = async (sourceUrl) => {
|
|
127
|
-
removeDirectoryOperation.throwIfAborted()
|
|
127
|
+
removeDirectoryOperation.throwIfAborted();
|
|
128
128
|
const sourceStats = await readEntryStat(sourceUrl, {
|
|
129
129
|
nullIfNotFound: true,
|
|
130
130
|
followLink: false,
|
|
131
|
-
})
|
|
131
|
+
});
|
|
132
132
|
|
|
133
133
|
// file/directory not found
|
|
134
134
|
if (sourceStats === null) {
|
|
135
|
-
return
|
|
135
|
+
return;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
if (
|
|
@@ -140,25 +140,25 @@ const removeDirectory = async (
|
|
|
140
140
|
sourceStats.isCharacterDevice() ||
|
|
141
141
|
sourceStats.isBlockDevice()
|
|
142
142
|
) {
|
|
143
|
-
await visitFile(sourceUrl)
|
|
143
|
+
await visitFile(sourceUrl);
|
|
144
144
|
} else if (sourceStats.isSymbolicLink()) {
|
|
145
|
-
await visitSymbolicLink(sourceUrl)
|
|
145
|
+
await visitSymbolicLink(sourceUrl);
|
|
146
146
|
} else if (sourceStats.isDirectory()) {
|
|
147
|
-
await visitDirectory(`${sourceUrl}/`)
|
|
147
|
+
await visitDirectory(`${sourceUrl}/`);
|
|
148
148
|
}
|
|
149
|
-
}
|
|
149
|
+
};
|
|
150
150
|
|
|
151
151
|
const visitDirectory = async (directoryUrl) => {
|
|
152
|
-
const directoryPath = urlToFileSystemPath(directoryUrl)
|
|
152
|
+
const directoryPath = urlToFileSystemPath(directoryUrl);
|
|
153
153
|
const optionsFromRecursive = recursive
|
|
154
154
|
? {
|
|
155
155
|
handleNotEmptyError: async () => {
|
|
156
|
-
await removeDirectoryContent(directoryUrl)
|
|
157
|
-
await visitDirectory(directoryUrl)
|
|
156
|
+
await removeDirectoryContent(directoryUrl);
|
|
157
|
+
await visitDirectory(directoryUrl);
|
|
158
158
|
},
|
|
159
159
|
}
|
|
160
|
-
: {}
|
|
161
|
-
removeDirectoryOperation.throwIfAborted()
|
|
160
|
+
: {};
|
|
161
|
+
removeDirectoryOperation.throwIfAborted();
|
|
162
162
|
await removeDirectoryNaive(directoryPath, {
|
|
163
163
|
...optionsFromRecursive,
|
|
164
164
|
// Workaround for https://github.com/joyent/node/issues/4337
|
|
@@ -167,64 +167,64 @@ const removeDirectory = async (
|
|
|
167
167
|
handlePermissionError: async (error) => {
|
|
168
168
|
console.error(
|
|
169
169
|
`trying to fix windows EPERM after readir on ${directoryPath}`,
|
|
170
|
-
)
|
|
170
|
+
);
|
|
171
171
|
|
|
172
|
-
let openOrCloseError
|
|
172
|
+
let openOrCloseError;
|
|
173
173
|
try {
|
|
174
|
-
const fd = openSync(directoryPath)
|
|
175
|
-
closeSync(fd)
|
|
174
|
+
const fd = openSync(directoryPath);
|
|
175
|
+
closeSync(fd);
|
|
176
176
|
} catch (e) {
|
|
177
|
-
openOrCloseError = e
|
|
177
|
+
openOrCloseError = e;
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
if (openOrCloseError) {
|
|
181
181
|
if (openOrCloseError.code === "ENOENT") {
|
|
182
|
-
return
|
|
182
|
+
return;
|
|
183
183
|
}
|
|
184
184
|
console.error(
|
|
185
185
|
`error while trying to fix windows EPERM after readir on ${directoryPath}: ${openOrCloseError.stack}`,
|
|
186
|
-
)
|
|
187
|
-
throw error
|
|
186
|
+
);
|
|
187
|
+
throw error;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
await removeDirectoryNaive(directoryPath, {
|
|
191
191
|
...optionsFromRecursive,
|
|
192
|
-
})
|
|
192
|
+
});
|
|
193
193
|
},
|
|
194
194
|
}
|
|
195
195
|
: {}),
|
|
196
|
-
})
|
|
197
|
-
}
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
198
|
|
|
199
199
|
const removeDirectoryContent = async (directoryUrl) => {
|
|
200
|
-
removeDirectoryOperation.throwIfAborted()
|
|
201
|
-
const names = await readDirectory(directoryUrl)
|
|
200
|
+
removeDirectoryOperation.throwIfAborted();
|
|
201
|
+
const names = await readDirectory(directoryUrl);
|
|
202
202
|
await Promise.all(
|
|
203
203
|
names.map(async (name) => {
|
|
204
|
-
const url = resolveUrl(name, directoryUrl)
|
|
205
|
-
await visit(url)
|
|
204
|
+
const url = resolveUrl(name, directoryUrl);
|
|
205
|
+
await visit(url);
|
|
206
206
|
}),
|
|
207
|
-
)
|
|
208
|
-
}
|
|
207
|
+
);
|
|
208
|
+
};
|
|
209
209
|
|
|
210
210
|
const visitFile = async (fileUrl) => {
|
|
211
|
-
await removeNonDirectory(fileUrl, { maxRetries, retryDelay })
|
|
212
|
-
}
|
|
211
|
+
await removeNonDirectory(fileUrl, { maxRetries, retryDelay });
|
|
212
|
+
};
|
|
213
213
|
|
|
214
214
|
const visitSymbolicLink = async (symbolicLinkUrl) => {
|
|
215
|
-
await removeNonDirectory(symbolicLinkUrl, { maxRetries, retryDelay })
|
|
216
|
-
}
|
|
215
|
+
await removeNonDirectory(symbolicLinkUrl, { maxRetries, retryDelay });
|
|
216
|
+
};
|
|
217
217
|
|
|
218
218
|
try {
|
|
219
219
|
if (onlyContent) {
|
|
220
|
-
await removeDirectoryContent(rootDirectoryUrl)
|
|
220
|
+
await removeDirectoryContent(rootDirectoryUrl);
|
|
221
221
|
} else {
|
|
222
|
-
await visitDirectory(rootDirectoryUrl)
|
|
222
|
+
await visitDirectory(rootDirectoryUrl);
|
|
223
223
|
}
|
|
224
224
|
} finally {
|
|
225
|
-
await removeDirectoryOperation.end()
|
|
225
|
+
await removeDirectoryOperation.end();
|
|
226
226
|
}
|
|
227
|
-
}
|
|
227
|
+
};
|
|
228
228
|
|
|
229
229
|
const removeDirectoryNaive = (
|
|
230
230
|
directoryPath,
|
|
@@ -234,9 +234,9 @@ const removeDirectoryNaive = (
|
|
|
234
234
|
rmdir(directoryPath, (error, lstatObject) => {
|
|
235
235
|
if (error) {
|
|
236
236
|
if (handlePermissionError && error.code === "EPERM") {
|
|
237
|
-
resolve(handlePermissionError(error))
|
|
237
|
+
resolve(handlePermissionError(error));
|
|
238
238
|
} else if (error.code === "ENOENT") {
|
|
239
|
-
resolve()
|
|
239
|
+
resolve();
|
|
240
240
|
} else if (
|
|
241
241
|
handleNotEmptyError &&
|
|
242
242
|
// linux os
|
|
@@ -244,13 +244,13 @@ const removeDirectoryNaive = (
|
|
|
244
244
|
// SunOS
|
|
245
245
|
error.code === "EEXIST")
|
|
246
246
|
) {
|
|
247
|
-
resolve(handleNotEmptyError(error))
|
|
247
|
+
resolve(handleNotEmptyError(error));
|
|
248
248
|
} else {
|
|
249
|
-
reject(error)
|
|
249
|
+
reject(error);
|
|
250
250
|
}
|
|
251
251
|
} else {
|
|
252
|
-
resolve(lstatObject)
|
|
252
|
+
resolve(lstatObject);
|
|
253
253
|
}
|
|
254
|
-
})
|
|
255
|
-
})
|
|
256
|
-
}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
};
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { promises, constants } from "node:fs"
|
|
1
|
+
import { promises, constants } from "node:fs";
|
|
2
2
|
|
|
3
|
-
import { assertAndNormalizeFileUrl } from "./file_url_validation.js"
|
|
3
|
+
import { assertAndNormalizeFileUrl } from "./file_url_validation.js";
|
|
4
4
|
|
|
5
|
-
const { access } = promises
|
|
5
|
+
const { access } = promises;
|
|
6
6
|
const {
|
|
7
7
|
// F_OK,
|
|
8
8
|
R_OK,
|
|
9
9
|
W_OK,
|
|
10
10
|
X_OK,
|
|
11
|
-
} = constants
|
|
11
|
+
} = constants;
|
|
12
12
|
|
|
13
13
|
export const testEntryPermissions = async (
|
|
14
14
|
source,
|
|
@@ -19,24 +19,24 @@ export const testEntryPermissions = async (
|
|
|
19
19
|
allowedIfNotFound = false,
|
|
20
20
|
} = {},
|
|
21
21
|
) => {
|
|
22
|
-
const sourceUrl = assertAndNormalizeFileUrl(source)
|
|
23
|
-
let binaryFlags = 0
|
|
22
|
+
const sourceUrl = assertAndNormalizeFileUrl(source);
|
|
23
|
+
let binaryFlags = 0;
|
|
24
24
|
|
|
25
25
|
// if (visible) binaryFlags |= F_OK
|
|
26
|
-
if (read) binaryFlags |= R_OK
|
|
27
|
-
if (write) binaryFlags |= W_OK
|
|
28
|
-
if (execute) binaryFlags |= X_OK
|
|
26
|
+
if (read) binaryFlags |= R_OK;
|
|
27
|
+
if (write) binaryFlags |= W_OK;
|
|
28
|
+
if (execute) binaryFlags |= X_OK;
|
|
29
29
|
|
|
30
30
|
try {
|
|
31
|
-
await access(new URL(sourceUrl), binaryFlags)
|
|
32
|
-
return true
|
|
31
|
+
await access(new URL(sourceUrl), binaryFlags);
|
|
32
|
+
return true;
|
|
33
33
|
} catch (error) {
|
|
34
34
|
if (error.code === "ENOENT") {
|
|
35
35
|
if (allowedIfNotFound) {
|
|
36
|
-
return true
|
|
36
|
+
return true;
|
|
37
37
|
}
|
|
38
|
-
throw error
|
|
38
|
+
throw error;
|
|
39
39
|
}
|
|
40
|
-
return false
|
|
40
|
+
return false;
|
|
41
41
|
}
|
|
42
|
-
}
|
|
42
|
+
};
|
package/src/writeDirectory.js
CHANGED
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
import { promises } from "node:fs"
|
|
2
|
-
import { urlToFileSystemPath } from "@jsenv/urls"
|
|
1
|
+
import { promises } from "node:fs";
|
|
2
|
+
import { urlToFileSystemPath } from "@jsenv/urls";
|
|
3
3
|
|
|
4
|
-
import { assertAndNormalizeDirectoryUrl } from "./directory_url_validation.js"
|
|
5
|
-
import { statsToType } from "./internal/statsToType.js"
|
|
6
|
-
import { readEntryStat } from "./readEntryStat.js"
|
|
4
|
+
import { assertAndNormalizeDirectoryUrl } from "./directory_url_validation.js";
|
|
5
|
+
import { statsToType } from "./internal/statsToType.js";
|
|
6
|
+
import { readEntryStat } from "./readEntryStat.js";
|
|
7
7
|
|
|
8
8
|
// https://nodejs.org/dist/latest-v13.x/docs/api/fs.html#fs_fspromises_mkdir_path_options
|
|
9
|
-
const { mkdir } = promises
|
|
9
|
+
const { mkdir } = promises;
|
|
10
10
|
|
|
11
11
|
export const writeDirectory = async (
|
|
12
12
|
destination,
|
|
13
13
|
{ recursive = true, allowUseless = false } = {},
|
|
14
14
|
) => {
|
|
15
|
-
const destinationUrl = assertAndNormalizeDirectoryUrl(destination)
|
|
16
|
-
const destinationPath = urlToFileSystemPath(destinationUrl)
|
|
15
|
+
const destinationUrl = assertAndNormalizeDirectoryUrl(destination);
|
|
16
|
+
const destinationPath = urlToFileSystemPath(destinationUrl);
|
|
17
17
|
|
|
18
18
|
const destinationStats = await readEntryStat(destinationUrl, {
|
|
19
19
|
nullIfNotFound: true,
|
|
20
20
|
followLink: false,
|
|
21
|
-
})
|
|
21
|
+
});
|
|
22
22
|
|
|
23
23
|
if (destinationStats) {
|
|
24
24
|
if (destinationStats.isDirectory()) {
|
|
25
25
|
if (allowUseless) {
|
|
26
|
-
return
|
|
26
|
+
return;
|
|
27
27
|
}
|
|
28
|
-
throw new Error(`directory already exists at ${destinationPath}`)
|
|
28
|
+
throw new Error(`directory already exists at ${destinationPath}`);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const destinationType = statsToType(destinationStats)
|
|
31
|
+
const destinationType = statsToType(destinationStats);
|
|
32
32
|
throw new Error(
|
|
33
33
|
`cannot write directory at ${destinationPath} because there is a ${destinationType}`,
|
|
34
|
-
)
|
|
34
|
+
);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
try {
|
|
38
|
-
await mkdir(destinationPath, { recursive })
|
|
38
|
+
await mkdir(destinationPath, { recursive });
|
|
39
39
|
} catch (error) {
|
|
40
40
|
if (allowUseless && error.code === "EEXIST") {
|
|
41
|
-
return
|
|
41
|
+
return;
|
|
42
42
|
}
|
|
43
|
-
throw error
|
|
43
|
+
throw error;
|
|
44
44
|
}
|
|
45
|
-
}
|
|
45
|
+
};
|