@jsenv/filesystem 4.2.3 → 4.2.5
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,13 +1,13 @@
|
|
|
1
|
-
import { Abort } from "@jsenv/abort"
|
|
2
|
-
import { urlToFileSystemPath, resolveUrl } from "@jsenv/urls"
|
|
1
|
+
import { Abort } from "@jsenv/abort";
|
|
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 { moveEntry } from "./moveEntry.js"
|
|
9
|
-
import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js"
|
|
10
|
-
import { statsToType } from "./internal/statsToType.js"
|
|
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 { moveEntry } from "./moveEntry.js";
|
|
9
|
+
import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js";
|
|
10
|
+
import { statsToType } from "./internal/statsToType.js";
|
|
11
11
|
|
|
12
12
|
export const moveDirectoryContent = async ({
|
|
13
13
|
signal = new AbortController().signal,
|
|
@@ -16,23 +16,23 @@ export const moveDirectoryContent = async ({
|
|
|
16
16
|
overwrite,
|
|
17
17
|
followLink = true,
|
|
18
18
|
} = {}) => {
|
|
19
|
-
const fromUrl = assertAndNormalizeDirectoryUrl(from)
|
|
20
|
-
const fromPath = urlToFileSystemPath(fromUrl)
|
|
21
|
-
let toUrl = assertAndNormalizeDirectoryUrl(to)
|
|
22
|
-
let toPath = urlToFileSystemPath(toUrl)
|
|
19
|
+
const fromUrl = assertAndNormalizeDirectoryUrl(from);
|
|
20
|
+
const fromPath = urlToFileSystemPath(fromUrl);
|
|
21
|
+
let toUrl = assertAndNormalizeDirectoryUrl(to);
|
|
22
|
+
let toPath = urlToFileSystemPath(toUrl);
|
|
23
23
|
|
|
24
24
|
const sourceStats = await readEntryStat(fromUrl, {
|
|
25
25
|
nullIfNotFound: true,
|
|
26
26
|
followLink: false,
|
|
27
|
-
})
|
|
27
|
+
});
|
|
28
28
|
if (!sourceStats) {
|
|
29
|
-
throw new Error(`no directory to move content from at ${fromPath}`)
|
|
29
|
+
throw new Error(`no directory to move content from at ${fromPath}`);
|
|
30
30
|
}
|
|
31
31
|
if (!sourceStats.isDirectory()) {
|
|
32
|
-
const sourceType = statsToType(sourceStats)
|
|
32
|
+
const sourceType = statsToType(sourceStats);
|
|
33
33
|
throw new Error(
|
|
34
34
|
`found a ${sourceType} instead of a directory at ${fromPath}`,
|
|
35
|
-
)
|
|
35
|
+
);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
let destinationStats = await readEntryStat(toUrl, {
|
|
@@ -40,41 +40,41 @@ export const moveDirectoryContent = async ({
|
|
|
40
40
|
// we force false here but in fact we will follow the destination link
|
|
41
41
|
// to know where we will actually move and detect useless move overrite etc..
|
|
42
42
|
followLink: false,
|
|
43
|
-
})
|
|
43
|
+
});
|
|
44
44
|
if (followLink && destinationStats && destinationStats.isSymbolicLink()) {
|
|
45
|
-
const linkTarget = await readSymbolicLink(toUrl)
|
|
46
|
-
toUrl = resolveUrl(linkTarget, toUrl)
|
|
47
|
-
toPath = urlToFileSystemPath(toUrl)
|
|
45
|
+
const linkTarget = await readSymbolicLink(toUrl);
|
|
46
|
+
toUrl = resolveUrl(linkTarget, toUrl);
|
|
47
|
+
toPath = urlToFileSystemPath(toUrl);
|
|
48
48
|
destinationStats = await readEntryStat(toUrl, {
|
|
49
49
|
nullIfNotFound: true,
|
|
50
|
-
})
|
|
50
|
+
});
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
if (destinationStats === null) {
|
|
54
|
-
throw new Error(`no directory to move content into at ${toPath}`)
|
|
54
|
+
throw new Error(`no directory to move content into at ${toPath}`);
|
|
55
55
|
}
|
|
56
56
|
if (!destinationStats.isDirectory()) {
|
|
57
|
-
const destinationType = statsToType(destinationStats)
|
|
57
|
+
const destinationType = statsToType(destinationStats);
|
|
58
58
|
throw new Error(
|
|
59
59
|
`destination leads to a ${destinationType} instead of a directory at ${toPath}`,
|
|
60
|
-
)
|
|
60
|
+
);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
if (urlTargetsSameFileSystemPath(fromUrl, toUrl)) {
|
|
64
64
|
throw new Error(
|
|
65
65
|
`cannot move directory content, source and destination are the same (${fromPath})`,
|
|
66
|
-
)
|
|
66
|
+
);
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const moveOperation = Abort.startOperation()
|
|
70
|
-
moveOperation.addAbortSignal(signal)
|
|
69
|
+
const moveOperation = Abort.startOperation();
|
|
70
|
+
moveOperation.addAbortSignal(signal);
|
|
71
71
|
try {
|
|
72
|
-
moveOperation.throwIfAborted()
|
|
73
|
-
const directoryEntries = await readDirectory(fromUrl)
|
|
72
|
+
moveOperation.throwIfAborted();
|
|
73
|
+
const directoryEntries = await readDirectory(fromUrl);
|
|
74
74
|
await Promise.all(
|
|
75
75
|
directoryEntries.map(async (directoryEntry) => {
|
|
76
|
-
const from = resolveUrl(directoryEntry, fromUrl)
|
|
77
|
-
const to = resolveUrl(directoryEntry, toUrl)
|
|
76
|
+
const from = resolveUrl(directoryEntry, fromUrl);
|
|
77
|
+
const to = resolveUrl(directoryEntry, toUrl);
|
|
78
78
|
|
|
79
79
|
await moveOperation.withSignal(async (signal) => {
|
|
80
80
|
await moveEntry({
|
|
@@ -83,11 +83,11 @@ export const moveDirectoryContent = async ({
|
|
|
83
83
|
to,
|
|
84
84
|
overwrite,
|
|
85
85
|
followLink,
|
|
86
|
-
})
|
|
87
|
-
})
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
88
|
}),
|
|
89
|
-
)
|
|
89
|
+
);
|
|
90
90
|
} finally {
|
|
91
|
-
await moveOperation.end()
|
|
91
|
+
await moveOperation.end();
|
|
92
92
|
}
|
|
93
|
-
}
|
|
93
|
+
};
|
package/src/moveEntry.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { rename } from "node:fs"
|
|
2
|
-
import { Abort } from "@jsenv/abort"
|
|
3
|
-
import { urlToFileSystemPath, resolveUrl } from "@jsenv/urls"
|
|
1
|
+
import { rename } from "node:fs";
|
|
2
|
+
import { Abort } from "@jsenv/abort";
|
|
3
|
+
import { urlToFileSystemPath, resolveUrl } from "@jsenv/urls";
|
|
4
4
|
|
|
5
|
-
import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js"
|
|
6
|
-
import { statsToType } from "./internal/statsToType.js"
|
|
7
|
-
import { assertAndNormalizeFileUrl } from "./file_url_validation.js"
|
|
8
|
-
import { ensureParentDirectories } from "./ensureParentDirectories.js"
|
|
9
|
-
import { removeEntry } from "./removeEntry.js"
|
|
10
|
-
import { copyEntry } from "./copyEntry.js"
|
|
11
|
-
import { readEntryStat } from "./readEntryStat.js"
|
|
12
|
-
import { readSymbolicLink } from "./readSymbolicLink.js"
|
|
5
|
+
import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js";
|
|
6
|
+
import { statsToType } from "./internal/statsToType.js";
|
|
7
|
+
import { assertAndNormalizeFileUrl } from "./file_url_validation.js";
|
|
8
|
+
import { ensureParentDirectories } from "./ensureParentDirectories.js";
|
|
9
|
+
import { removeEntry } from "./removeEntry.js";
|
|
10
|
+
import { copyEntry } from "./copyEntry.js";
|
|
11
|
+
import { readEntryStat } from "./readEntryStat.js";
|
|
12
|
+
import { readSymbolicLink } from "./readSymbolicLink.js";
|
|
13
13
|
|
|
14
14
|
export const moveEntry = async ({
|
|
15
15
|
signal = new AbortController().signal,
|
|
@@ -19,17 +19,17 @@ export const moveEntry = async ({
|
|
|
19
19
|
allowUseless = false,
|
|
20
20
|
followLink = true,
|
|
21
21
|
}) => {
|
|
22
|
-
const fromUrl = assertAndNormalizeFileUrl(from)
|
|
23
|
-
const fromPath = urlToFileSystemPath(fromUrl)
|
|
24
|
-
let toUrl = assertAndNormalizeFileUrl(to)
|
|
25
|
-
let toPath = urlToFileSystemPath(toUrl)
|
|
22
|
+
const fromUrl = assertAndNormalizeFileUrl(from);
|
|
23
|
+
const fromPath = urlToFileSystemPath(fromUrl);
|
|
24
|
+
let toUrl = assertAndNormalizeFileUrl(to);
|
|
25
|
+
let toPath = urlToFileSystemPath(toUrl);
|
|
26
26
|
|
|
27
27
|
const sourceStats = await readEntryStat(fromUrl, {
|
|
28
28
|
nullIfNotFound: true,
|
|
29
29
|
followLink: false,
|
|
30
|
-
})
|
|
30
|
+
});
|
|
31
31
|
if (!sourceStats) {
|
|
32
|
-
throw new Error(`nothing to move from ${fromPath}`)
|
|
32
|
+
throw new Error(`nothing to move from ${fromPath}`);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
let destinationStats = await readEntryStat(toUrl, {
|
|
@@ -37,72 +37,72 @@ export const moveEntry = async ({
|
|
|
37
37
|
// we force false here but in fact we will follow the destination link
|
|
38
38
|
// to know where we will actually move and detect useless move overrite etc..
|
|
39
39
|
followLink: false,
|
|
40
|
-
})
|
|
40
|
+
});
|
|
41
41
|
|
|
42
42
|
if (followLink && destinationStats && destinationStats.isSymbolicLink()) {
|
|
43
|
-
const linkTarget = await readSymbolicLink(toUrl)
|
|
44
|
-
toUrl = resolveUrl(linkTarget, toUrl)
|
|
45
|
-
toPath = urlToFileSystemPath(toUrl)
|
|
43
|
+
const linkTarget = await readSymbolicLink(toUrl);
|
|
44
|
+
toUrl = resolveUrl(linkTarget, toUrl);
|
|
45
|
+
toPath = urlToFileSystemPath(toUrl);
|
|
46
46
|
destinationStats = await readEntryStat(toUrl, {
|
|
47
47
|
nullIfNotFound: true,
|
|
48
|
-
})
|
|
48
|
+
});
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
if (urlTargetsSameFileSystemPath(fromUrl, toUrl)) {
|
|
52
52
|
if (allowUseless) {
|
|
53
|
-
return
|
|
53
|
+
return;
|
|
54
54
|
}
|
|
55
55
|
throw new Error(
|
|
56
56
|
`no move needed for ${fromPath} because destination and source are the same`,
|
|
57
|
-
)
|
|
57
|
+
);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
const moveOperation = Abort.startOperation()
|
|
61
|
-
moveOperation.addAbortSignal(signal)
|
|
60
|
+
const moveOperation = Abort.startOperation();
|
|
61
|
+
moveOperation.addAbortSignal(signal);
|
|
62
62
|
|
|
63
63
|
try {
|
|
64
64
|
if (destinationStats) {
|
|
65
|
-
const sourceType = statsToType(sourceStats)
|
|
66
|
-
const destinationType = statsToType(destinationStats)
|
|
65
|
+
const sourceType = statsToType(sourceStats);
|
|
66
|
+
const destinationType = statsToType(destinationStats);
|
|
67
67
|
|
|
68
68
|
if (sourceType !== destinationType) {
|
|
69
69
|
throw new Error(
|
|
70
70
|
`cannot move ${sourceType} from ${fromPath} to ${toPath} because destination exists and is not a ${sourceType} (it's a ${destinationType})`,
|
|
71
|
-
)
|
|
71
|
+
);
|
|
72
72
|
}
|
|
73
73
|
if (!overwrite) {
|
|
74
74
|
throw new Error(
|
|
75
75
|
`cannot move ${sourceType} from ${fromPath} to ${toPath} because destination exists and overwrite option is disabled`,
|
|
76
|
-
)
|
|
76
|
+
);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
// remove file, link, directory...
|
|
80
80
|
await removeEntry(toUrl, {
|
|
81
81
|
signal: moveOperation.signal,
|
|
82
82
|
recursive: true,
|
|
83
|
-
})
|
|
83
|
+
});
|
|
84
84
|
} else {
|
|
85
|
-
await ensureParentDirectories(toUrl)
|
|
85
|
+
await ensureParentDirectories(toUrl);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
moveOperation.throwIfAborted()
|
|
88
|
+
moveOperation.throwIfAborted();
|
|
89
89
|
await moveNaive(fromPath, toPath, {
|
|
90
90
|
handleCrossDeviceError: async () => {
|
|
91
91
|
await copyEntry({
|
|
92
92
|
from: fromUrl,
|
|
93
93
|
to: toUrl,
|
|
94
94
|
preserveStat: true,
|
|
95
|
-
})
|
|
95
|
+
});
|
|
96
96
|
await removeEntry(fromUrl, {
|
|
97
97
|
signal: moveOperation.signal,
|
|
98
98
|
recursive: true,
|
|
99
|
-
})
|
|
99
|
+
});
|
|
100
100
|
},
|
|
101
|
-
})
|
|
101
|
+
});
|
|
102
102
|
} finally {
|
|
103
|
-
await moveOperation.end()
|
|
103
|
+
await moveOperation.end();
|
|
104
104
|
}
|
|
105
|
-
}
|
|
105
|
+
};
|
|
106
106
|
|
|
107
107
|
const moveNaive = (
|
|
108
108
|
fromPath,
|
|
@@ -113,13 +113,13 @@ const moveNaive = (
|
|
|
113
113
|
rename(fromPath, destinationPath, (error) => {
|
|
114
114
|
if (error) {
|
|
115
115
|
if (handleCrossDeviceError && error.code === "EXDEV") {
|
|
116
|
-
resolve(handleCrossDeviceError(error))
|
|
116
|
+
resolve(handleCrossDeviceError(error));
|
|
117
117
|
} else {
|
|
118
|
-
reject(error)
|
|
118
|
+
reject(error);
|
|
119
119
|
}
|
|
120
120
|
} else {
|
|
121
|
-
resolve()
|
|
121
|
+
resolve();
|
|
122
122
|
}
|
|
123
|
-
})
|
|
124
|
-
})
|
|
125
|
-
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
};
|
package/src/readDirectory.js
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import { readdir } from "node:fs"
|
|
2
|
-
import { assertAndNormalizeDirectoryUrl } from "./directory_url_validation.js"
|
|
1
|
+
import { readdir } from "node:fs";
|
|
2
|
+
import { assertAndNormalizeDirectoryUrl } from "./directory_url_validation.js";
|
|
3
3
|
|
|
4
4
|
export const readDirectory = async (url, { emfileMaxWait = 1000 } = {}) => {
|
|
5
|
-
const directoryUrl = assertAndNormalizeDirectoryUrl(url)
|
|
6
|
-
const directoryUrlObject = new URL(directoryUrl)
|
|
7
|
-
const startMs = Date.now()
|
|
8
|
-
let attemptCount = 0
|
|
5
|
+
const directoryUrl = assertAndNormalizeDirectoryUrl(url);
|
|
6
|
+
const directoryUrlObject = new URL(directoryUrl);
|
|
7
|
+
const startMs = Date.now();
|
|
8
|
+
let attemptCount = 0;
|
|
9
9
|
|
|
10
10
|
const attempt = async () => {
|
|
11
11
|
try {
|
|
12
12
|
const names = await new Promise((resolve, reject) => {
|
|
13
13
|
readdir(directoryUrlObject, (error, names) => {
|
|
14
14
|
if (error) {
|
|
15
|
-
reject(error)
|
|
15
|
+
reject(error);
|
|
16
16
|
} else {
|
|
17
|
-
resolve(names)
|
|
17
|
+
resolve(names);
|
|
18
18
|
}
|
|
19
|
-
})
|
|
20
|
-
})
|
|
21
|
-
return names.map(encodeURIComponent)
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
return names.map(encodeURIComponent);
|
|
22
22
|
} catch (e) {
|
|
23
23
|
// https://nodejs.org/dist/latest-v13.x/docs/api/errors.html#errors_common_system_errors
|
|
24
24
|
if (e.code === "EMFILE" || e.code === "ENFILE") {
|
|
25
|
-
attemptCount
|
|
26
|
-
const nowMs = Date.now()
|
|
27
|
-
const timeSpentWaiting = nowMs - startMs
|
|
25
|
+
attemptCount++;
|
|
26
|
+
const nowMs = Date.now();
|
|
27
|
+
const timeSpentWaiting = nowMs - startMs;
|
|
28
28
|
if (timeSpentWaiting > emfileMaxWait) {
|
|
29
|
-
throw e
|
|
29
|
+
throw e;
|
|
30
30
|
}
|
|
31
|
-
await new Promise((resolve) => setTimeout(resolve), attemptCount)
|
|
32
|
-
return await attempt()
|
|
31
|
+
await new Promise((resolve) => setTimeout(resolve), attemptCount);
|
|
32
|
+
return await attempt();
|
|
33
33
|
}
|
|
34
|
-
throw e
|
|
34
|
+
throw e;
|
|
35
35
|
}
|
|
36
|
-
}
|
|
36
|
+
};
|
|
37
37
|
|
|
38
|
-
return attempt()
|
|
39
|
-
}
|
|
38
|
+
return attempt();
|
|
39
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readEntryStat } from "./readEntryStat.js"
|
|
1
|
+
import { readEntryStat } from "./readEntryStat.js";
|
|
2
2
|
|
|
3
3
|
export const readEntryModificationTime = async (source) => {
|
|
4
|
-
const stats = await readEntryStat(source)
|
|
5
|
-
return Math.floor(stats.mtimeMs)
|
|
6
|
-
}
|
|
4
|
+
const stats = await readEntryStat(source);
|
|
5
|
+
return Math.floor(stats.mtimeMs);
|
|
6
|
+
};
|
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
* https://nodejs.org/docs/latest-v13.x/api/fs.html#fs_file_modes
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { promises } from "node:fs"
|
|
7
|
-
import { urlToFileSystemPath } from "@jsenv/urls"
|
|
6
|
+
import { promises } from "node:fs";
|
|
7
|
+
import { urlToFileSystemPath } from "@jsenv/urls";
|
|
8
8
|
|
|
9
|
-
import { binaryFlagsToPermissions } from "./internal/permissions.js"
|
|
10
|
-
import { assertAndNormalizeFileUrl } from "./file_url_validation.js"
|
|
9
|
+
import { binaryFlagsToPermissions } from "./internal/permissions.js";
|
|
10
|
+
import { assertAndNormalizeFileUrl } from "./file_url_validation.js";
|
|
11
11
|
|
|
12
|
-
const { stat } = promises
|
|
12
|
+
const { stat } = promises;
|
|
13
13
|
|
|
14
14
|
export const readEntryPermissions = async (source) => {
|
|
15
|
-
const sourceUrl = assertAndNormalizeFileUrl(source)
|
|
16
|
-
const sourcePath = urlToFileSystemPath(sourceUrl)
|
|
15
|
+
const sourceUrl = assertAndNormalizeFileUrl(source);
|
|
16
|
+
const sourcePath = urlToFileSystemPath(sourceUrl);
|
|
17
17
|
|
|
18
|
-
const { mode } = await stat(sourcePath)
|
|
19
|
-
return binaryFlagsToPermissions(mode)
|
|
20
|
-
}
|
|
18
|
+
const { mode } = await stat(sourcePath);
|
|
19
|
+
return binaryFlagsToPermissions(mode);
|
|
20
|
+
};
|
package/src/readEntryStat.js
CHANGED
|
@@ -3,28 +3,28 @@
|
|
|
3
3
|
* https://nodejs.org/docs/latest-v13.x/api/fs.html#fs_class_fs_stats
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { lstat, stat } from "node:fs"
|
|
7
|
-
import { urlToFileSystemPath } from "@jsenv/urls"
|
|
6
|
+
import { lstat, stat } from "node:fs";
|
|
7
|
+
import { urlToFileSystemPath } from "@jsenv/urls";
|
|
8
8
|
|
|
9
|
-
import { assertAndNormalizeFileUrl } from "./file_url_validation.js"
|
|
10
|
-
import { writeEntryPermissions } from "./writeEntryPermissions.js"
|
|
9
|
+
import { assertAndNormalizeFileUrl } from "./file_url_validation.js";
|
|
10
|
+
import { writeEntryPermissions } from "./writeEntryPermissions.js";
|
|
11
11
|
|
|
12
|
-
const isWindows = process.platform === "win32"
|
|
12
|
+
const isWindows = process.platform === "win32";
|
|
13
13
|
|
|
14
14
|
export const readEntryStat = async (
|
|
15
15
|
source,
|
|
16
16
|
{ nullIfNotFound = false, followLink = true } = {},
|
|
17
17
|
) => {
|
|
18
|
-
let sourceUrl = assertAndNormalizeFileUrl(source)
|
|
19
|
-
if (sourceUrl.endsWith("/")) sourceUrl = sourceUrl.slice(0, -1)
|
|
18
|
+
let sourceUrl = assertAndNormalizeFileUrl(source);
|
|
19
|
+
if (sourceUrl.endsWith("/")) sourceUrl = sourceUrl.slice(0, -1);
|
|
20
20
|
|
|
21
|
-
const sourcePath = urlToFileSystemPath(sourceUrl)
|
|
21
|
+
const sourcePath = urlToFileSystemPath(sourceUrl);
|
|
22
22
|
|
|
23
23
|
const handleNotFoundOption = nullIfNotFound
|
|
24
24
|
? {
|
|
25
25
|
handleNotFoundError: () => null,
|
|
26
26
|
}
|
|
27
|
-
: {}
|
|
27
|
+
: {};
|
|
28
28
|
|
|
29
29
|
return readStat(sourcePath, {
|
|
30
30
|
followLink,
|
|
@@ -35,34 +35,34 @@ export const readEntryStat = async (
|
|
|
35
35
|
handlePermissionDeniedError: async (error) => {
|
|
36
36
|
console.error(
|
|
37
37
|
`trying to fix windows EPERM after stats on ${sourcePath}`,
|
|
38
|
-
)
|
|
38
|
+
);
|
|
39
39
|
|
|
40
40
|
try {
|
|
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 writeEntryPermissions(sourceUrl, 0o666)
|
|
44
|
+
await writeEntryPermissions(sourceUrl, 0o666);
|
|
45
45
|
const stats = await readStat(sourcePath, {
|
|
46
46
|
followLink,
|
|
47
47
|
...handleNotFoundOption,
|
|
48
48
|
// could not fix the permission error, give up and throw original error
|
|
49
49
|
handlePermissionDeniedError: () => {
|
|
50
|
-
console.error(`still got EPERM after stats on ${sourcePath}`)
|
|
51
|
-
throw error
|
|
50
|
+
console.error(`still got EPERM after stats on ${sourcePath}`);
|
|
51
|
+
throw error;
|
|
52
52
|
},
|
|
53
|
-
})
|
|
54
|
-
return stats
|
|
53
|
+
});
|
|
54
|
+
return stats;
|
|
55
55
|
} catch (e) {
|
|
56
56
|
console.error(
|
|
57
57
|
`error while trying to fix windows EPERM after stats on ${sourcePath}: ${e.stack}`,
|
|
58
|
-
)
|
|
59
|
-
throw error
|
|
58
|
+
);
|
|
59
|
+
throw error;
|
|
60
60
|
}
|
|
61
61
|
},
|
|
62
62
|
}
|
|
63
63
|
: {}),
|
|
64
|
-
})
|
|
65
|
-
}
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
66
|
|
|
67
67
|
const readStat = (
|
|
68
68
|
sourcePath,
|
|
@@ -72,24 +72,24 @@ const readStat = (
|
|
|
72
72
|
handlePermissionDeniedError = null,
|
|
73
73
|
} = {},
|
|
74
74
|
) => {
|
|
75
|
-
const nodeMethod = followLink ? stat : lstat
|
|
75
|
+
const nodeMethod = followLink ? stat : lstat;
|
|
76
76
|
|
|
77
77
|
return new Promise((resolve, reject) => {
|
|
78
78
|
nodeMethod(sourcePath, (error, statsObject) => {
|
|
79
79
|
if (error) {
|
|
80
80
|
if (handleNotFoundError && error.code === "ENOENT") {
|
|
81
|
-
resolve(handleNotFoundError(error))
|
|
81
|
+
resolve(handleNotFoundError(error));
|
|
82
82
|
} else if (
|
|
83
83
|
handlePermissionDeniedError &&
|
|
84
84
|
(error.code === "EPERM" || error.code === "EACCES")
|
|
85
85
|
) {
|
|
86
|
-
resolve(handlePermissionDeniedError(error))
|
|
86
|
+
resolve(handlePermissionDeniedError(error));
|
|
87
87
|
} else {
|
|
88
|
-
reject(error)
|
|
88
|
+
reject(error);
|
|
89
89
|
}
|
|
90
90
|
} else {
|
|
91
|
-
resolve(statsObject)
|
|
91
|
+
resolve(statsObject);
|
|
92
92
|
}
|
|
93
|
-
})
|
|
94
|
-
})
|
|
95
|
-
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
};
|
package/src/readFile.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { readFile as readFileNode } from "node:fs"
|
|
1
|
+
import { readFile as readFileNode } from "node:fs";
|
|
2
2
|
|
|
3
|
-
import { assertAndNormalizeFileUrl } from "./file_url_validation.js"
|
|
3
|
+
import { assertAndNormalizeFileUrl } from "./file_url_validation.js";
|
|
4
4
|
|
|
5
5
|
export const readFile = async (value, { as = "buffer" } = {}) => {
|
|
6
|
-
const fileUrl = assertAndNormalizeFileUrl(value)
|
|
6
|
+
const fileUrl = assertAndNormalizeFileUrl(value);
|
|
7
7
|
const buffer = await new Promise((resolve, reject) => {
|
|
8
8
|
readFileNode(new URL(fileUrl), (error, buffer) => {
|
|
9
9
|
if (error) {
|
|
10
|
-
reject(error)
|
|
10
|
+
reject(error);
|
|
11
11
|
} else {
|
|
12
|
-
resolve(buffer)
|
|
12
|
+
resolve(buffer);
|
|
13
13
|
}
|
|
14
|
-
})
|
|
15
|
-
})
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
16
|
if (as === "buffer") {
|
|
17
|
-
return buffer
|
|
17
|
+
return buffer;
|
|
18
18
|
}
|
|
19
19
|
if (as === "string") {
|
|
20
|
-
return buffer.toString()
|
|
20
|
+
return buffer.toString();
|
|
21
21
|
}
|
|
22
22
|
if (as === "json") {
|
|
23
|
-
return JSON.parse(buffer.toString())
|
|
23
|
+
return JSON.parse(buffer.toString());
|
|
24
24
|
}
|
|
25
25
|
throw new Error(
|
|
26
26
|
`"as" must be one of "buffer","string","json" received "${as}"`,
|
|
27
|
-
)
|
|
28
|
-
}
|
|
27
|
+
);
|
|
28
|
+
};
|
package/src/readFileSync.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { readFileSync as readFileSyncNode } from "node:fs"
|
|
1
|
+
import { readFileSync as readFileSyncNode } from "node:fs";
|
|
2
2
|
|
|
3
|
-
import { assertAndNormalizeFileUrl } from "./file_url_validation.js"
|
|
3
|
+
import { assertAndNormalizeFileUrl } from "./file_url_validation.js";
|
|
4
4
|
|
|
5
5
|
export const readFileSync = (value, { as = "buffer" } = {}) => {
|
|
6
|
-
const fileUrl = assertAndNormalizeFileUrl(value)
|
|
7
|
-
const buffer = readFileSyncNode(new URL(fileUrl))
|
|
6
|
+
const fileUrl = assertAndNormalizeFileUrl(value);
|
|
7
|
+
const buffer = readFileSyncNode(new URL(fileUrl));
|
|
8
8
|
if (as === "buffer") {
|
|
9
|
-
return buffer
|
|
9
|
+
return buffer;
|
|
10
10
|
}
|
|
11
11
|
if (as === "string") {
|
|
12
|
-
return buffer.toString()
|
|
12
|
+
return buffer.toString();
|
|
13
13
|
}
|
|
14
14
|
if (as === "json") {
|
|
15
|
-
return JSON.parse(buffer.toString())
|
|
15
|
+
return JSON.parse(buffer.toString());
|
|
16
16
|
}
|
|
17
17
|
throw new Error(
|
|
18
18
|
`"as" must be one of "buffer","string","json" received "${as}"`,
|
|
19
|
-
)
|
|
20
|
-
}
|
|
19
|
+
);
|
|
20
|
+
};
|
package/src/readSymbolicLink.js
CHANGED
|
@@ -3,25 +3,25 @@
|
|
|
3
3
|
* https://nodejs.org/docs/latest-v13.x/api/fs.html#fs_fs_symlink_target_path_type_callback
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { readlink } from "node:fs"
|
|
7
|
-
import { isFileSystemPath, fileSystemPathToUrl } from "@jsenv/urls"
|
|
6
|
+
import { readlink } from "node:fs";
|
|
7
|
+
import { isFileSystemPath, fileSystemPathToUrl } from "@jsenv/urls";
|
|
8
8
|
|
|
9
|
-
import { assertAndNormalizeFileUrl } from "./file_url_validation.js"
|
|
9
|
+
import { assertAndNormalizeFileUrl } from "./file_url_validation.js";
|
|
10
10
|
|
|
11
11
|
export const readSymbolicLink = (url) => {
|
|
12
|
-
const symbolicLinkUrl = assertAndNormalizeFileUrl(url)
|
|
12
|
+
const symbolicLinkUrl = assertAndNormalizeFileUrl(url);
|
|
13
13
|
|
|
14
14
|
return new Promise((resolve, reject) => {
|
|
15
15
|
readlink(new URL(symbolicLinkUrl), (error, resolvedPath) => {
|
|
16
16
|
if (error) {
|
|
17
|
-
reject(error)
|
|
17
|
+
reject(error);
|
|
18
18
|
} else {
|
|
19
19
|
resolve(
|
|
20
20
|
isFileSystemPath(resolvedPath)
|
|
21
21
|
? fileSystemPathToUrl(resolvedPath)
|
|
22
22
|
: resolvedPath.replace(/\\/g, "/"), // replace back slashes with slashes
|
|
23
|
-
)
|
|
23
|
+
);
|
|
24
24
|
}
|
|
25
|
-
})
|
|
26
|
-
})
|
|
27
|
-
}
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
};
|