@jsenv/filesystem 2.7.2 → 3.2.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/README.md +1 -1
- package/main.js +14 -12
- package/package.json +3 -4
- package/src/assertDirectoryPresence.js +2 -2
- package/src/assertFilePresence.js +2 -2
- package/src/collectDirectoryMatchReport.js +2 -2
- package/src/collectFiles.js +2 -2
- package/src/copyDirectoryContent.js +6 -6
- package/src/{copyFileSystemNode.js → copyEntry.js} +15 -18
- package/src/ensureEmptyDirectory.js +4 -4
- package/src/ensureParentDirectories.js +2 -1
- package/src/fileSystemRootUrl.js +2 -0
- package/src/grantPermissionsOnEntry.js +20 -0
- package/src/internal/createWatcher.js +1 -1
- package/src/internal/guard_second_call.js +20 -0
- package/src/internal/read_entry_info.js +20 -0
- package/src/moveDirectoryContent.js +6 -6
- package/src/{moveFileSystemNode.js → moveEntry.js} +10 -10
- package/src/moveUrl.js +10 -0
- package/src/readEntryModificationTime.js +6 -0
- package/src/{readFileSystemNodePermissions.js → readEntryPermissions.js} +1 -1
- package/src/{readFileSystemNodeStat.js → readEntryStat.js} +3 -3
- package/src/readFile.js +8 -7
- package/src/readFileSync.js +20 -0
- package/src/registerDirectoryLifecycle.js +201 -151
- package/src/registerFileLifecycle.js +39 -6
- package/src/{removeFileSystemNode.js → removeEntry.js} +4 -4
- package/src/{testFileSystemNodePermissions.js → testEntryPermissions.js} +3 -2
- package/src/writeDirectory.js +4 -3
- package/src/{writeFileSystemNodeModificationTime.js → writeEntryModificationTime.js} +3 -2
- package/src/{writeFileSystemNodePermissions.js → writeEntryPermissions.js} +3 -2
- package/src/writeFile.js +5 -6
- package/src/writeFileSync.js +20 -0
- package/src/writeSymbolicLink.js +4 -4
- package/src/grantPermissionsOnFileSystemNode.js +0 -20
- package/src/internal/fileSystemNodeToTypeOrNull.js +0 -18
- package/src/readFileSystemNodeModificationTime.js +0 -6
package/README.md
CHANGED
|
@@ -64,7 +64,7 @@ import { registerDirectoryLifecycle } from "@jsenv/filesystem"
|
|
|
64
64
|
|
|
65
65
|
const directoryContentDescription = {}
|
|
66
66
|
const unregister = registerDirectoryLifecycle("file:///directory/", {
|
|
67
|
-
|
|
67
|
+
watchPatterns: {
|
|
68
68
|
"./**/*": true,
|
|
69
69
|
"./node_modules/": false,
|
|
70
70
|
},
|
package/main.js
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/* eslint-disable import/max-dependencies */
|
|
2
|
-
|
|
3
1
|
// we won't internalize @jsenv/url-meta
|
|
4
2
|
// so that @jsenv/url-meta does not becomes nodejs specific
|
|
5
3
|
// but these functions could be inside this repository
|
|
@@ -21,28 +19,31 @@ export { comparePathnames } from "./src/comparePathnames.js"
|
|
|
21
19
|
export { ensureEmptyDirectory } from "./src/ensureEmptyDirectory.js"
|
|
22
20
|
export { ensureParentDirectories } from "./src/ensureParentDirectories.js"
|
|
23
21
|
export { ensureWindowsDriveLetter } from "./src/ensureWindowsDriveLetter.js"
|
|
24
|
-
export {
|
|
22
|
+
export { copyEntry } from "./src/copyEntry.js"
|
|
25
23
|
export { copyDirectoryContent } from "./src/copyDirectoryContent.js"
|
|
26
24
|
export { fileSystemPathToUrl } from "./src/fileSystemPathToUrl.js"
|
|
25
|
+
export { fileSystemRootUrl } from "./src/fileSystemRootUrl.js"
|
|
27
26
|
export { getRealFileSystemUrlSync } from "./src/getRealFileSystemUrlSync.js"
|
|
28
|
-
export {
|
|
27
|
+
export { grantPermissionsOnEntry } from "./src/grantPermissionsOnEntry.js"
|
|
29
28
|
export { isFileSystemPath } from "./src/isFileSystemPath.js"
|
|
30
29
|
export { listFilesMatching } from "./src/listFilesMatching.js"
|
|
31
30
|
export { memoize } from "./src/memoize.js"
|
|
32
31
|
export { moveDirectoryContent } from "./src/moveDirectoryContent.js"
|
|
33
|
-
export {
|
|
32
|
+
export { moveEntry } from "./src/moveEntry.js"
|
|
33
|
+
export { moveUrl } from "./src/moveUrl.js"
|
|
34
34
|
export { readDirectory } from "./src/readDirectory.js"
|
|
35
35
|
export { readFile } from "./src/readFile.js"
|
|
36
|
-
export {
|
|
37
|
-
export {
|
|
38
|
-
export {
|
|
36
|
+
export { readFileSync } from "./src/readFileSync.js"
|
|
37
|
+
export { readEntryModificationTime } from "./src/readEntryModificationTime.js"
|
|
38
|
+
export { readEntryPermissions } from "./src/readEntryPermissions.js"
|
|
39
|
+
export { readEntryStat } from "./src/readEntryStat.js"
|
|
39
40
|
export { readSymbolicLink } from "./src/readSymbolicLink.js"
|
|
40
41
|
export { registerDirectoryLifecycle } from "./src/registerDirectoryLifecycle.js"
|
|
41
42
|
export { registerFileLifecycle } from "./src/registerFileLifecycle.js"
|
|
42
|
-
export {
|
|
43
|
+
export { removeEntry } from "./src/removeEntry.js"
|
|
43
44
|
export { resolveDirectoryUrl } from "./src/resolveDirectoryUrl.js"
|
|
44
45
|
export { resolveUrl } from "./src/resolveUrl.js"
|
|
45
|
-
export {
|
|
46
|
+
export { testEntryPermissions } from "./src/testEntryPermissions.js"
|
|
46
47
|
export { urlIsInsideOf } from "./src/urlIsInsideOf.js"
|
|
47
48
|
export { urlToBasename } from "./src/urlToBasename.js"
|
|
48
49
|
export { urlToExtension } from "./src/urlToExtension.js"
|
|
@@ -56,6 +57,7 @@ export { urlToRessource } from "./src/urlToRessource.js"
|
|
|
56
57
|
export { urlToScheme } from "./src/urlToScheme.js"
|
|
57
58
|
export { writeDirectory } from "./src/writeDirectory.js"
|
|
58
59
|
export { writeFile } from "./src/writeFile.js"
|
|
59
|
-
export {
|
|
60
|
-
export {
|
|
60
|
+
export { writeFileSync } from "./src/writeFileSync.js"
|
|
61
|
+
export { writeEntryModificationTime } from "./src/writeEntryModificationTime.js"
|
|
62
|
+
export { writeEntryPermissions } from "./src/writeEntryPermissions.js"
|
|
61
63
|
export { writeSymbolicLink } from "./src/writeSymbolicLink.js"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/filesystem",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Collection of functions to interact with filesystem in Node.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
"node": ">=16.13.0"
|
|
17
17
|
},
|
|
18
18
|
"publishConfig": {
|
|
19
|
-
"access": "public"
|
|
20
|
-
"registry": "https://registry.npmjs.org"
|
|
19
|
+
"access": "public"
|
|
21
20
|
},
|
|
22
21
|
"type": "module",
|
|
23
22
|
"exports": {
|
|
@@ -56,4 +55,4 @@
|
|
|
56
55
|
"eslint-plugin-import": "2.25.4",
|
|
57
56
|
"prettier": "2.5.1"
|
|
58
57
|
}
|
|
59
|
-
}
|
|
58
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { statsToType } from "./internal/statsToType.js"
|
|
2
2
|
import { assertAndNormalizeFileUrl } from "./assertAndNormalizeFileUrl.js"
|
|
3
3
|
import { urlToFileSystemPath } from "./urlToFileSystemPath.js"
|
|
4
|
-
import {
|
|
4
|
+
import { readEntryStat } from "./readEntryStat.js"
|
|
5
5
|
|
|
6
6
|
export const assertDirectoryPresence = async (source) => {
|
|
7
7
|
const sourceUrl = assertAndNormalizeFileUrl(source)
|
|
8
8
|
const sourcePath = urlToFileSystemPath(sourceUrl)
|
|
9
9
|
|
|
10
|
-
const sourceStats = await
|
|
10
|
+
const sourceStats = await readEntryStat(sourceUrl, {
|
|
11
11
|
nullIfNotFound: true,
|
|
12
12
|
})
|
|
13
13
|
if (!sourceStats) {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { statsToType } from "./internal/statsToType.js"
|
|
2
2
|
import { assertAndNormalizeFileUrl } from "./assertAndNormalizeFileUrl.js"
|
|
3
3
|
import { urlToFileSystemPath } from "./urlToFileSystemPath.js"
|
|
4
|
-
import {
|
|
4
|
+
import { readEntryStat } from "./readEntryStat.js"
|
|
5
5
|
|
|
6
6
|
export const assertFilePresence = async (source) => {
|
|
7
7
|
const sourceUrl = assertAndNormalizeFileUrl(source)
|
|
8
8
|
const sourcePath = urlToFileSystemPath(sourceUrl)
|
|
9
9
|
|
|
10
|
-
const sourceStats = await
|
|
10
|
+
const sourceStats = await readEntryStat(sourceUrl, {
|
|
11
11
|
nullIfNotFound: true,
|
|
12
12
|
})
|
|
13
13
|
if (!sourceStats) {
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import { ensureUrlTrailingSlash } from "./internal/ensureUrlTrailingSlash.js"
|
|
9
9
|
import { assertAndNormalizeDirectoryUrl } from "./assertAndNormalizeDirectoryUrl.js"
|
|
10
10
|
import { readDirectory } from "./readDirectory.js"
|
|
11
|
-
import {
|
|
11
|
+
import { readEntryStat } from "./readEntryStat.js"
|
|
12
12
|
import { urlToRelativeUrl } from "./urlToRelativeUrl.js"
|
|
13
13
|
import { comparePathnames } from "./comparePathnames.js"
|
|
14
14
|
|
|
@@ -46,7 +46,7 @@ export const collectDirectoryMatchReport = async ({
|
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
collectOperation.throwIfAborted()
|
|
49
|
-
const directoryChildNodeStats = await
|
|
49
|
+
const directoryChildNodeStats = await readEntryStat(
|
|
50
50
|
directoryChildNodeUrl,
|
|
51
51
|
{
|
|
52
52
|
// we ignore symlink because recursively traversed
|
package/src/collectFiles.js
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
|
|
8
8
|
import { assertAndNormalizeDirectoryUrl } from "./assertAndNormalizeDirectoryUrl.js"
|
|
9
9
|
import { readDirectory } from "./readDirectory.js"
|
|
10
|
-
import {
|
|
10
|
+
import { readEntryStat } from "./readEntryStat.js"
|
|
11
11
|
import { urlToRelativeUrl } from "./urlToRelativeUrl.js"
|
|
12
12
|
import { comparePathnames } from "./comparePathnames.js"
|
|
13
13
|
|
|
@@ -39,7 +39,7 @@ export const collectFiles = async ({
|
|
|
39
39
|
const directoryChildNodeUrl = `${directoryUrl}${directoryItem}`
|
|
40
40
|
|
|
41
41
|
collectOperation.throwIfAborted()
|
|
42
|
-
const directoryChildNodeStats = await
|
|
42
|
+
const directoryChildNodeStats = await readEntryStat(
|
|
43
43
|
directoryChildNodeUrl,
|
|
44
44
|
{
|
|
45
45
|
// we ignore symlink because recursively traversed
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Abort } from "@jsenv/abort"
|
|
2
2
|
|
|
3
3
|
import { assertAndNormalizeDirectoryUrl } from "./assertAndNormalizeDirectoryUrl.js"
|
|
4
|
-
import {
|
|
4
|
+
import { readEntryStat } from "./readEntryStat.js"
|
|
5
5
|
import { urlToFileSystemPath } from "./urlToFileSystemPath.js"
|
|
6
6
|
import { readSymbolicLink } from "./readSymbolicLink.js"
|
|
7
7
|
import { resolveUrl } from "./resolveUrl.js"
|
|
8
8
|
import { readDirectory } from "./readDirectory.js"
|
|
9
|
-
import {
|
|
9
|
+
import { copyEntry } from "./copyEntry.js"
|
|
10
10
|
import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js"
|
|
11
11
|
import { statsToType } from "./internal/statsToType.js"
|
|
12
12
|
|
|
@@ -22,7 +22,7 @@ export const copyDirectoryContent = async ({
|
|
|
22
22
|
let toUrl = assertAndNormalizeDirectoryUrl(to)
|
|
23
23
|
let toPath = urlToFileSystemPath(toUrl)
|
|
24
24
|
|
|
25
|
-
const sourceStats = await
|
|
25
|
+
const sourceStats = await readEntryStat(fromUrl, {
|
|
26
26
|
nullIfNotFound: true,
|
|
27
27
|
followLink: false,
|
|
28
28
|
})
|
|
@@ -36,7 +36,7 @@ export const copyDirectoryContent = async ({
|
|
|
36
36
|
)
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
let destinationStats = await
|
|
39
|
+
let destinationStats = await readEntryStat(toUrl, {
|
|
40
40
|
nullIfNotFound: true,
|
|
41
41
|
// we force false here but in fact we will follow the destination link
|
|
42
42
|
// to know where we will actually move and detect useless move overrite etc..
|
|
@@ -46,7 +46,7 @@ export const copyDirectoryContent = async ({
|
|
|
46
46
|
const linkTarget = await readSymbolicLink(toUrl)
|
|
47
47
|
toUrl = resolveUrl(linkTarget, toUrl)
|
|
48
48
|
toPath = urlToFileSystemPath(toUrl)
|
|
49
|
-
destinationStats = await
|
|
49
|
+
destinationStats = await readEntryStat(toUrl, {
|
|
50
50
|
nullIfNotFound: true,
|
|
51
51
|
})
|
|
52
52
|
}
|
|
@@ -77,7 +77,7 @@ export const copyDirectoryContent = async ({
|
|
|
77
77
|
const from = resolveUrl(directoryEntry, fromUrl)
|
|
78
78
|
const to = resolveUrl(directoryEntry, toUrl)
|
|
79
79
|
await copyOperation.withSignal(async (signal) => {
|
|
80
|
-
await
|
|
80
|
+
await copyEntry({
|
|
81
81
|
signal,
|
|
82
82
|
from,
|
|
83
83
|
to,
|
|
@@ -9,18 +9,18 @@ import { binaryFlagsToPermissions } from "./internal/permissions.js"
|
|
|
9
9
|
import { assertAndNormalizeFileUrl } from "./assertAndNormalizeFileUrl.js"
|
|
10
10
|
import { writeDirectory } from "./writeDirectory.js"
|
|
11
11
|
import { urlToRelativeUrl } from "./urlToRelativeUrl.js"
|
|
12
|
-
import {
|
|
12
|
+
import { readEntryStat } from "./readEntryStat.js"
|
|
13
13
|
import { ensureParentDirectories } from "./ensureParentDirectories.js"
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
14
|
+
import { writeEntryPermissions } from "./writeEntryPermissions.js"
|
|
15
|
+
import { writeEntryModificationTime } from "./writeEntryModificationTime.js"
|
|
16
16
|
import { readDirectory } from "./readDirectory.js"
|
|
17
17
|
import { readSymbolicLink } from "./readSymbolicLink.js"
|
|
18
18
|
import { writeSymbolicLink } from "./writeSymbolicLink.js"
|
|
19
19
|
import { urlIsInsideOf } from "./urlIsInsideOf.js"
|
|
20
|
-
import {
|
|
20
|
+
import { removeEntry } from "./removeEntry.js"
|
|
21
21
|
import { urlToFileSystemPath } from "./urlToFileSystemPath.js"
|
|
22
22
|
|
|
23
|
-
export const
|
|
23
|
+
export const copyEntry = async ({
|
|
24
24
|
signal = new AbortController().signal,
|
|
25
25
|
from,
|
|
26
26
|
to,
|
|
@@ -36,7 +36,7 @@ export const copyFileSystemNode = async ({
|
|
|
36
36
|
let toUrl = assertAndNormalizeFileUrl(to)
|
|
37
37
|
let toPath = urlToFileSystemPath(toUrl)
|
|
38
38
|
|
|
39
|
-
const sourceStats = await
|
|
39
|
+
const sourceStats = await readEntryStat(fromUrl, {
|
|
40
40
|
nullIfNotFound: true,
|
|
41
41
|
followLink: false,
|
|
42
42
|
})
|
|
@@ -44,7 +44,7 @@ export const copyFileSystemNode = async ({
|
|
|
44
44
|
throw new Error(`nothing to copy at ${fromPath}`)
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
let destinationStats = await
|
|
47
|
+
let destinationStats = await readEntryStat(toUrl, {
|
|
48
48
|
nullIfNotFound: true,
|
|
49
49
|
// we force false here but in fact we will follow the destination link
|
|
50
50
|
// to know where we will actually move and detect useless move overrite etc..
|
|
@@ -55,7 +55,7 @@ export const copyFileSystemNode = async ({
|
|
|
55
55
|
const linkTarget = await readSymbolicLink(toUrl)
|
|
56
56
|
toUrl = resolveUrl(linkTarget, toUrl)
|
|
57
57
|
toPath = urlToFileSystemPath(toUrl)
|
|
58
|
-
destinationStats = await
|
|
58
|
+
destinationStats = await readEntryStat(toUrl, {
|
|
59
59
|
nullIfNotFound: true,
|
|
60
60
|
})
|
|
61
61
|
}
|
|
@@ -130,7 +130,7 @@ export const copyFileSystemNode = async ({
|
|
|
130
130
|
// we must guess ourself the type of the symlink
|
|
131
131
|
// because the destination might not exists because not yet copied
|
|
132
132
|
// https://nodejs.org/dist/latest-v13.x/docs/api/fs.html#fs_fs_symlink_target_path_type_callback
|
|
133
|
-
const targetStats = await
|
|
133
|
+
const targetStats = await readEntryStat(symbolicLinkTargetUrl, {
|
|
134
134
|
nullIfNotFound: true,
|
|
135
135
|
followLink: false,
|
|
136
136
|
})
|
|
@@ -148,13 +148,10 @@ export const copyFileSystemNode = async ({
|
|
|
148
148
|
if (preservePermissions || preserveMtime) {
|
|
149
149
|
const { mode, mtimeMs } = stats
|
|
150
150
|
if (preservePermissions) {
|
|
151
|
-
await
|
|
152
|
-
toUrl,
|
|
153
|
-
binaryFlagsToPermissions(mode),
|
|
154
|
-
)
|
|
151
|
+
await writeEntryPermissions(toUrl, binaryFlagsToPermissions(mode))
|
|
155
152
|
}
|
|
156
153
|
if (preserveMtime) {
|
|
157
|
-
await
|
|
154
|
+
await writeEntryModificationTime(toUrl, mtimeMs)
|
|
158
155
|
}
|
|
159
156
|
}
|
|
160
157
|
}
|
|
@@ -172,11 +169,11 @@ export const copyFileSystemNode = async ({
|
|
|
172
169
|
const names = await readDirectory(directoryUrl)
|
|
173
170
|
await Promise.all(
|
|
174
171
|
names.map(async (name) => {
|
|
175
|
-
const
|
|
176
|
-
const stats = await
|
|
172
|
+
const entryUrl = resolveUrl(name, directoryUrl)
|
|
173
|
+
const stats = await readEntryStat(entryUrl, {
|
|
177
174
|
followLink: false,
|
|
178
175
|
})
|
|
179
|
-
await visit(
|
|
176
|
+
await visit(entryUrl, stats)
|
|
180
177
|
}),
|
|
181
178
|
)
|
|
182
179
|
}
|
|
@@ -198,7 +195,7 @@ export const copyFileSystemNode = async ({
|
|
|
198
195
|
}
|
|
199
196
|
|
|
200
197
|
// remove file, link, directory...
|
|
201
|
-
await
|
|
198
|
+
await removeEntry(toUrl, {
|
|
202
199
|
signal: copyOperation.signal,
|
|
203
200
|
recursive: true,
|
|
204
201
|
allowUseless: true,
|
|
@@ -2,11 +2,11 @@ import { statsToType } from "./internal/statsToType.js"
|
|
|
2
2
|
import { assertAndNormalizeFileUrl } from "./assertAndNormalizeFileUrl.js"
|
|
3
3
|
import { urlToFileSystemPath } from "./urlToFileSystemPath.js"
|
|
4
4
|
import { writeDirectory } from "./writeDirectory.js"
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { readEntryStat } from "./readEntryStat.js"
|
|
6
|
+
import { removeEntry } from "./removeEntry.js"
|
|
7
7
|
|
|
8
8
|
export const ensureEmptyDirectory = async (source) => {
|
|
9
|
-
const stats = await
|
|
9
|
+
const stats = await readEntryStat(source, {
|
|
10
10
|
nullIfNotFound: true,
|
|
11
11
|
followLink: false,
|
|
12
12
|
})
|
|
@@ -16,7 +16,7 @@ export const ensureEmptyDirectory = async (source) => {
|
|
|
16
16
|
}
|
|
17
17
|
if (stats.isDirectory()) {
|
|
18
18
|
// if there is a directory remove its content and done
|
|
19
|
-
return
|
|
19
|
+
return removeEntry(source, {
|
|
20
20
|
allowUseless: true,
|
|
21
21
|
recursive: true,
|
|
22
22
|
onlyContent: true,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { assertAndNormalizeFileUrl } from "./assertAndNormalizeFileUrl.js"
|
|
2
|
+
import { readEntryPermissions } from "./readEntryPermissions.js"
|
|
3
|
+
import { writeEntryPermissions } from "./writeEntryPermissions.js"
|
|
4
|
+
|
|
5
|
+
export const grantPermissionsOnEntry = async (
|
|
6
|
+
source,
|
|
7
|
+
{ read = false, write = false, execute = false },
|
|
8
|
+
) => {
|
|
9
|
+
const sourceUrl = assertAndNormalizeFileUrl(source)
|
|
10
|
+
|
|
11
|
+
const filePermissions = await readEntryPermissions(sourceUrl)
|
|
12
|
+
await writeEntryPermissions(sourceUrl, {
|
|
13
|
+
owner: { read, write, execute },
|
|
14
|
+
group: { read, write, execute },
|
|
15
|
+
others: { read, write, execute },
|
|
16
|
+
})
|
|
17
|
+
return async () => {
|
|
18
|
+
await writeEntryPermissions(sourceUrl, filePermissions)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const guardTooFastSecondCall = (
|
|
2
|
+
callback,
|
|
3
|
+
cooldownBetweenFileEvents = 40,
|
|
4
|
+
) => {
|
|
5
|
+
const previousCallMsMap = new Map()
|
|
6
|
+
return (fileEvent) => {
|
|
7
|
+
const { relativeUrl } = fileEvent
|
|
8
|
+
const previousCallMs = previousCallMsMap.get(relativeUrl)
|
|
9
|
+
const nowMs = Date.now()
|
|
10
|
+
if (previousCallMs) {
|
|
11
|
+
const msEllapsed = nowMs - previousCallMs
|
|
12
|
+
if (msEllapsed < cooldownBetweenFileEvents) {
|
|
13
|
+
previousCallMsMap.delete(relativeUrl)
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
previousCallMsMap.set(relativeUrl, nowMs)
|
|
18
|
+
callback(fileEvent)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { statSync } from "node:fs"
|
|
2
|
+
|
|
3
|
+
import { statsToType } from "./statsToType.js"
|
|
4
|
+
|
|
5
|
+
export const readEntryInfo = (url) => {
|
|
6
|
+
try {
|
|
7
|
+
const stats = statSync(new URL(url))
|
|
8
|
+
const type = statsToType(stats)
|
|
9
|
+
return {
|
|
10
|
+
type,
|
|
11
|
+
atimeMs: stats.atimeMs,
|
|
12
|
+
mtimeMs: stats.mtimeMs,
|
|
13
|
+
}
|
|
14
|
+
} catch (e) {
|
|
15
|
+
if (e.code === "ENOENT") {
|
|
16
|
+
return null
|
|
17
|
+
}
|
|
18
|
+
throw e
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Abort } from "@jsenv/abort"
|
|
2
2
|
|
|
3
3
|
import { assertAndNormalizeDirectoryUrl } from "./assertAndNormalizeDirectoryUrl.js"
|
|
4
|
-
import {
|
|
4
|
+
import { readEntryStat } from "./readEntryStat.js"
|
|
5
5
|
import { urlToFileSystemPath } from "./urlToFileSystemPath.js"
|
|
6
6
|
import { readSymbolicLink } from "./readSymbolicLink.js"
|
|
7
7
|
import { resolveUrl } from "./resolveUrl.js"
|
|
8
8
|
import { readDirectory } from "./readDirectory.js"
|
|
9
|
-
import {
|
|
9
|
+
import { moveEntry } from "./moveEntry.js"
|
|
10
10
|
import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js"
|
|
11
11
|
import { statsToType } from "./internal/statsToType.js"
|
|
12
12
|
|
|
@@ -22,7 +22,7 @@ export const moveDirectoryContent = async ({
|
|
|
22
22
|
let toUrl = assertAndNormalizeDirectoryUrl(to)
|
|
23
23
|
let toPath = urlToFileSystemPath(toUrl)
|
|
24
24
|
|
|
25
|
-
const sourceStats = await
|
|
25
|
+
const sourceStats = await readEntryStat(fromUrl, {
|
|
26
26
|
nullIfNotFound: true,
|
|
27
27
|
followLink: false,
|
|
28
28
|
})
|
|
@@ -36,7 +36,7 @@ export const moveDirectoryContent = async ({
|
|
|
36
36
|
)
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
let destinationStats = await
|
|
39
|
+
let destinationStats = await readEntryStat(toUrl, {
|
|
40
40
|
nullIfNotFound: true,
|
|
41
41
|
// we force false here but in fact we will follow the destination link
|
|
42
42
|
// to know where we will actually move and detect useless move overrite etc..
|
|
@@ -46,7 +46,7 @@ export const moveDirectoryContent = async ({
|
|
|
46
46
|
const linkTarget = await readSymbolicLink(toUrl)
|
|
47
47
|
toUrl = resolveUrl(linkTarget, toUrl)
|
|
48
48
|
toPath = urlToFileSystemPath(toUrl)
|
|
49
|
-
destinationStats = await
|
|
49
|
+
destinationStats = await readEntryStat(toUrl, {
|
|
50
50
|
nullIfNotFound: true,
|
|
51
51
|
})
|
|
52
52
|
}
|
|
@@ -78,7 +78,7 @@ export const moveDirectoryContent = async ({
|
|
|
78
78
|
const to = resolveUrl(directoryEntry, toUrl)
|
|
79
79
|
|
|
80
80
|
await moveOperation.withSignal(async (signal) => {
|
|
81
|
-
await
|
|
81
|
+
await moveEntry({
|
|
82
82
|
signal,
|
|
83
83
|
from,
|
|
84
84
|
to,
|
|
@@ -7,12 +7,12 @@ import { assertAndNormalizeFileUrl } from "./assertAndNormalizeFileUrl.js"
|
|
|
7
7
|
import { urlToFileSystemPath } from "./urlToFileSystemPath.js"
|
|
8
8
|
import { resolveUrl } from "./resolveUrl.js"
|
|
9
9
|
import { ensureParentDirectories } from "./ensureParentDirectories.js"
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
10
|
+
import { removeEntry } from "./removeEntry.js"
|
|
11
|
+
import { copyEntry } from "./copyEntry.js"
|
|
12
|
+
import { readEntryStat } from "./readEntryStat.js"
|
|
13
13
|
import { readSymbolicLink } from "./readSymbolicLink.js"
|
|
14
14
|
|
|
15
|
-
export const
|
|
15
|
+
export const moveEntry = async ({
|
|
16
16
|
signal = new AbortController().signal,
|
|
17
17
|
from,
|
|
18
18
|
to,
|
|
@@ -25,7 +25,7 @@ export const moveFileSystemNode = async ({
|
|
|
25
25
|
let toUrl = assertAndNormalizeFileUrl(to)
|
|
26
26
|
let toPath = urlToFileSystemPath(toUrl)
|
|
27
27
|
|
|
28
|
-
const sourceStats = await
|
|
28
|
+
const sourceStats = await readEntryStat(fromUrl, {
|
|
29
29
|
nullIfNotFound: true,
|
|
30
30
|
followLink: false,
|
|
31
31
|
})
|
|
@@ -33,7 +33,7 @@ export const moveFileSystemNode = async ({
|
|
|
33
33
|
throw new Error(`nothing to move from ${fromPath}`)
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
let destinationStats = await
|
|
36
|
+
let destinationStats = await readEntryStat(toUrl, {
|
|
37
37
|
nullIfNotFound: true,
|
|
38
38
|
// we force false here but in fact we will follow the destination link
|
|
39
39
|
// to know where we will actually move and detect useless move overrite etc..
|
|
@@ -44,7 +44,7 @@ export const moveFileSystemNode = async ({
|
|
|
44
44
|
const linkTarget = await readSymbolicLink(toUrl)
|
|
45
45
|
toUrl = resolveUrl(linkTarget, toUrl)
|
|
46
46
|
toPath = urlToFileSystemPath(toUrl)
|
|
47
|
-
destinationStats = await
|
|
47
|
+
destinationStats = await readEntryStat(toUrl, {
|
|
48
48
|
nullIfNotFound: true,
|
|
49
49
|
})
|
|
50
50
|
}
|
|
@@ -78,7 +78,7 @@ export const moveFileSystemNode = async ({
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// remove file, link, directory...
|
|
81
|
-
await
|
|
81
|
+
await removeEntry(toUrl, {
|
|
82
82
|
signal: moveOperation.signal,
|
|
83
83
|
recursive: true,
|
|
84
84
|
})
|
|
@@ -89,12 +89,12 @@ export const moveFileSystemNode = async ({
|
|
|
89
89
|
moveOperation.throwIfAborted()
|
|
90
90
|
await moveNaive(fromPath, toPath, {
|
|
91
91
|
handleCrossDeviceError: async () => {
|
|
92
|
-
await
|
|
92
|
+
await copyEntry({
|
|
93
93
|
from: fromUrl,
|
|
94
94
|
to: toUrl,
|
|
95
95
|
preserveStat: true,
|
|
96
96
|
})
|
|
97
|
-
await
|
|
97
|
+
await removeEntry(fromUrl, {
|
|
98
98
|
signal: moveOperation.signal,
|
|
99
99
|
recursive: true,
|
|
100
100
|
})
|
package/src/moveUrl.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { urlToRelativeUrl } from "./urlToRelativeUrl.js"
|
|
2
|
+
|
|
3
|
+
export const moveUrl = ({ url, from, to, preferAbsolute = false }) => {
|
|
4
|
+
const relativeUrl = urlToRelativeUrl(url, from)
|
|
5
|
+
const absoluteUrl = new URL(relativeUrl, to).href
|
|
6
|
+
if (preferAbsolute) {
|
|
7
|
+
return absoluteUrl
|
|
8
|
+
}
|
|
9
|
+
return urlToRelativeUrl(absoluteUrl, to)
|
|
10
|
+
}
|
|
@@ -11,7 +11,7 @@ import { urlToFileSystemPath } from "./urlToFileSystemPath.js"
|
|
|
11
11
|
|
|
12
12
|
const { stat } = promises
|
|
13
13
|
|
|
14
|
-
export const
|
|
14
|
+
export const readEntryPermissions = async (source) => {
|
|
15
15
|
const sourceUrl = assertAndNormalizeFileUrl(source)
|
|
16
16
|
const sourcePath = urlToFileSystemPath(sourceUrl)
|
|
17
17
|
|
|
@@ -7,11 +7,11 @@ import { lstat, stat } from "node:fs"
|
|
|
7
7
|
|
|
8
8
|
import { assertAndNormalizeFileUrl } from "./assertAndNormalizeFileUrl.js"
|
|
9
9
|
import { urlToFileSystemPath } from "./urlToFileSystemPath.js"
|
|
10
|
-
import {
|
|
10
|
+
import { writeEntryPermissions } from "./writeEntryPermissions.js"
|
|
11
11
|
|
|
12
12
|
const isWindows = process.platform === "win32"
|
|
13
13
|
|
|
14
|
-
export const
|
|
14
|
+
export const readEntryStat = async (
|
|
15
15
|
source,
|
|
16
16
|
{ nullIfNotFound = false, followLink = true } = {},
|
|
17
17
|
) => {
|
|
@@ -41,7 +41,7 @@ export const readFileSystemNodeStat = async (
|
|
|
41
41
|
// unfortunately it means we mutate the permissions
|
|
42
42
|
// without being able to restore them to the previous value
|
|
43
43
|
// (because reading current permission would also throw)
|
|
44
|
-
await
|
|
44
|
+
await writeEntryPermissions(sourceUrl, 0o666)
|
|
45
45
|
const stats = await readStat(sourcePath, {
|
|
46
46
|
followLink,
|
|
47
47
|
...handleNotFoundOption,
|
package/src/readFile.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { promisify } from "util"
|
|
2
|
-
import { readFile as readFileNode } from "fs"
|
|
1
|
+
import { promisify } from "node:util"
|
|
2
|
+
import { readFile as readFileNode } from "node:fs"
|
|
3
|
+
|
|
3
4
|
import { assertAndNormalizeFileUrl } from "./assertAndNormalizeFileUrl.js"
|
|
4
|
-
import { urlToFileSystemPath } from "./urlToFileSystemPath.js"
|
|
5
5
|
|
|
6
6
|
const readFilePromisified = promisify(readFileNode)
|
|
7
|
-
export const readFile = async (value, { as = "
|
|
7
|
+
export const readFile = async (value, { as = "buffer" } = {}) => {
|
|
8
8
|
const fileUrl = assertAndNormalizeFileUrl(value)
|
|
9
|
-
const
|
|
10
|
-
const buffer = await readFilePromisified(filePath)
|
|
9
|
+
const buffer = await readFilePromisified(new URL(fileUrl))
|
|
11
10
|
if (as === "buffer") {
|
|
12
11
|
return buffer
|
|
13
12
|
}
|
|
@@ -17,5 +16,7 @@ export const readFile = async (value, { as = "string" } = {}) => {
|
|
|
17
16
|
if (as === "json") {
|
|
18
17
|
return JSON.parse(buffer.toString())
|
|
19
18
|
}
|
|
20
|
-
throw new Error(
|
|
19
|
+
throw new Error(
|
|
20
|
+
`"as" must be one of "buffer","string","json" received "${as}"`,
|
|
21
|
+
)
|
|
21
22
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { readFileSync as readFileSyncNode } from "node:fs"
|
|
2
|
+
|
|
3
|
+
import { assertAndNormalizeFileUrl } from "./assertAndNormalizeFileUrl.js"
|
|
4
|
+
|
|
5
|
+
export const readFileSync = (value, { as = "buffer" } = {}) => {
|
|
6
|
+
const fileUrl = assertAndNormalizeFileUrl(value)
|
|
7
|
+
const buffer = readFileSyncNode(new URL(fileUrl))
|
|
8
|
+
if (as === "buffer") {
|
|
9
|
+
return buffer
|
|
10
|
+
}
|
|
11
|
+
if (as === "string") {
|
|
12
|
+
return buffer.toString()
|
|
13
|
+
}
|
|
14
|
+
if (as === "json") {
|
|
15
|
+
return JSON.parse(buffer.toString())
|
|
16
|
+
}
|
|
17
|
+
throw new Error(
|
|
18
|
+
`"as" must be one of "buffer","string","json" received "${as}"`,
|
|
19
|
+
)
|
|
20
|
+
}
|