@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.
- package/package.json +4 -3
- package/src/{assertDirectoryPresence.js → assert_directory_presence.js} +3 -3
- package/src/{assertFilePresence.js → assert_file_presence.js} +3 -3
- package/src/{copyDirectoryContent.js → copy/copy_directory_content.js} +7 -7
- package/src/copy/copy_directory_content_sync.js +90 -0
- package/src/copy/copy_directory_sync.js +9 -0
- package/src/{copyEntry.js → copy/copy_entry.js} +13 -13
- package/src/copy/copy_entry_sync.js +212 -0
- package/src/{registerDirectoryLifecycle.js → lifecycle/register_directory_lifecycle.js} +10 -9
- package/src/{registerFileLifecycle.js → lifecycle/register_file_lifecycle.js} +5 -5
- package/src/{collectDirectoryMatchReport.js → list/collect_directory_match_report.js} +5 -5
- package/src/{collectFiles.js → list/collect_files.js} +4 -4
- package/src/{listFilesMatching.js → list/list_files_matching.js} +1 -1
- package/src/main.js +78 -35
- package/src/{moveDirectoryContent.js → move/move_directory_content.js} +7 -7
- package/src/{moveEntry.js → move/move_entry.js} +8 -8
- package/src/{ensureParentDirectories.js → read_write/ensure_parent_directories.js} +3 -3
- package/src/read_write/ensure_parent_directories_sync.js +16 -0
- package/src/{readDirectory.js → read_write/read_directory.js} +2 -1
- package/src/read_write/read_directory_sync.js +10 -0
- package/src/{readFile.js → read_write/read_file.js} +1 -1
- package/src/read_write/read_file_structure_sync.js +57 -0
- package/src/{readFileSync.js → read_write/read_file_sync.js} +1 -1
- package/src/{readSymbolicLink.js → read_write/read_symbolic_link.js} +1 -1
- package/src/read_write/read_symbolic_link_sync.js +17 -0
- package/src/{grantPermissionsOnEntry.js → read_write/stat/grant_permissions_on_entry.js} +3 -3
- package/src/{internal → read_write/stat}/read_entry_info.js +1 -1
- package/src/{readEntryModificationTime.js → read_write/stat/read_entry_modification_time.js} +1 -1
- package/src/{readEntryPermissions.js → read_write/stat/read_entry_permissions.js} +2 -2
- package/src/{readEntryStat.js → read_write/stat/read_entry_stat.js} +2 -2
- package/src/read_write/stat/read_entry_stat_sync.js +92 -0
- package/src/{testEntryPermissions.js → read_write/stat/test_entry_permissions.js} +1 -1
- package/src/{writeEntryModificationTime.js → read_write/stat/write_entry_modification_time.js} +1 -1
- package/src/read_write/stat/write_entry_modification_time_sync.js +14 -0
- package/src/{writeEntryPermissions.js → read_write/stat/write_entry_permissions.js} +2 -2
- package/src/read_write/stat/write_entry_permissions_sync.js +77 -0
- package/src/{writeDirectory.js → read_write/write_directory.js} +3 -3
- package/src/read_write/write_directory_sync.js +42 -0
- package/src/{writeFile.js → read_write/write_file.js} +2 -2
- package/src/read_write/write_file_structure_sync.js +13 -0
- package/src/{writeFileSync.js → read_write/write_file_sync.js} +1 -1
- package/src/{writeSymbolicLink.js → read_write/write_symbolic_link.js} +5 -5
- package/src/read_write/write_symbolic_link_sync.js +115 -0
- package/src/remove/clear_directory_sync.js +68 -0
- package/src/{ensureEmptyDirectory.js → remove/ensure_empty_directory.js} +9 -7
- package/src/remove/ensure_empty_directory_sync.js +34 -0
- package/src/remove/remove_directory.js +8 -0
- package/src/remove/remove_directory_sync.js +8 -0
- package/src/{removeEntry.js → remove/remove_entry.js} +3 -3
- package/src/remove/remove_entry_sync.js +240 -0
- package/src/remove/remove_file.js +5 -0
- package/src/remove/remove_file_sync.js +5 -0
- package/src/internal/getCommandArgument.js +0 -27
- /package/src/{bufferToEtag.js → buffer_to_etag.js} +0 -0
- /package/src/{internal/createWatcher.js → lifecycle/create_watcher.js} +0 -0
- /package/src/{internal → lifecycle}/guard_second_call.js +0 -0
- /package/src/{internal → lifecycle}/track_resources.js +0 -0
- /package/src/{comparePathnames.js → path_and_url/compare_pathnames.js} +0 -0
- /package/src/{directory_url_validation.js → path_and_url/directory_url_validation.js} +0 -0
- /package/src/{ensureWindowsDriveLetter.js → path_and_url/ensure_windows_drive_letter.js} +0 -0
- /package/src/{fileSystemRootUrl.js → path_and_url/file_system_root_url.js} +0 -0
- /package/src/{file_url_validation.js → path_and_url/file_url_validation.js} +0 -0
- /package/src/{internal/urlTargetsSameFileSystemPath.js → path_and_url/url_targets_same_file_system_path.js} +0 -0
- /package/src/{getRealFileSystemUrlSync.js → read_write/get_real_file_system_url_sync.js} +0 -0
- /package/src/{internal → read_write/stat}/permissions.js +0 -0
- /package/src/{internal/statsToType.js → read_write/stat/stats_to_type.js} +0 -0
|
@@ -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 "
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
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 { readEntryStat } from "../read_write/stat/read_entry_stat.js";
|
|
7
|
+
import { statsToType } from "../read_write/stat/stats_to_type.js";
|
|
8
|
+
import { readSymbolicLink } from "../read_write/read_symbolic_link.js";
|
|
9
|
+
import { readDirectory } from "../read_write/read_directory.js";
|
|
10
|
+
import { moveEntry } from "./move_entry.js";
|
|
11
11
|
|
|
12
12
|
export const moveDirectoryContent = async ({
|
|
13
13
|
signal = new AbortController().signal,
|
|
@@ -2,14 +2,14 @@ import { rename } from "node:fs";
|
|
|
2
2
|
import { Abort } from "@jsenv/abort";
|
|
3
3
|
import { urlToFileSystemPath, resolveUrl } from "@jsenv/urls";
|
|
4
4
|
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
5
|
+
import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
|
|
6
|
+
import { urlTargetsSameFileSystemPath } from "../path_and_url/url_targets_same_file_system_path.js";
|
|
7
|
+
import { statsToType } from "../read_write/stat/stats_to_type.js";
|
|
8
|
+
import { readEntryStat } from "../read_write/stat/read_entry_stat.js";
|
|
9
|
+
import { ensureParentDirectories } from "../read_write/ensure_parent_directories.js";
|
|
10
|
+
import { readSymbolicLink } from "../read_write/read_symbolic_link.js";
|
|
11
|
+
import { copyEntry } from "../copy/copy_entry.js";
|
|
12
|
+
import { removeEntry } from "../remove/remove_entry.js";
|
|
13
13
|
|
|
14
14
|
export const moveEntry = async ({
|
|
15
15
|
signal = new AbortController().signal,
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { dirname } from "node:path";
|
|
2
2
|
import { urlToFileSystemPath } from "@jsenv/urls";
|
|
3
3
|
|
|
4
|
-
import { assertAndNormalizeFileUrl } from "
|
|
5
|
-
import { writeDirectory } from "./
|
|
4
|
+
import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
|
|
5
|
+
import { writeDirectory } from "./write_directory.js";
|
|
6
6
|
|
|
7
7
|
export const ensureParentDirectories = async (destination) => {
|
|
8
8
|
const destinationUrl = assertAndNormalizeFileUrl(destination);
|
|
9
9
|
const destinationPath = urlToFileSystemPath(destinationUrl);
|
|
10
10
|
const destinationParentPath = dirname(destinationPath);
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
await writeDirectory(destinationParentPath, {
|
|
13
13
|
recursive: true,
|
|
14
14
|
allowUseless: true,
|
|
15
15
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { dirname } from "node:path";
|
|
2
|
+
import { urlToFileSystemPath } from "@jsenv/urls";
|
|
3
|
+
|
|
4
|
+
import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
|
|
5
|
+
import { writeDirectorySync } from "./write_directory_sync.js";
|
|
6
|
+
|
|
7
|
+
export const ensureParentDirectoriesSync = (destination) => {
|
|
8
|
+
const destinationUrl = assertAndNormalizeFileUrl(destination);
|
|
9
|
+
const destinationPath = urlToFileSystemPath(destinationUrl);
|
|
10
|
+
const destinationParentPath = dirname(destinationPath);
|
|
11
|
+
|
|
12
|
+
writeDirectorySync(destinationParentPath, {
|
|
13
|
+
recursive: true,
|
|
14
|
+
allowUseless: true,
|
|
15
|
+
});
|
|
16
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readdir } from "node:fs";
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
import { assertAndNormalizeDirectoryUrl } from "../path_and_url/directory_url_validation.js";
|
|
3
4
|
|
|
4
5
|
export const readDirectory = async (url, { emfileMaxWait = 1000 } = {}) => {
|
|
5
6
|
const directoryUrl = assertAndNormalizeDirectoryUrl(url);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { readdirSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
import { assertAndNormalizeDirectoryUrl } from "../path_and_url/directory_url_validation.js";
|
|
4
|
+
|
|
5
|
+
export const readDirectorySync = (url) => {
|
|
6
|
+
const directoryUrl = assertAndNormalizeDirectoryUrl(url);
|
|
7
|
+
const directoryUrlObject = new URL(directoryUrl);
|
|
8
|
+
const names = readdirSync(directoryUrlObject);
|
|
9
|
+
return names.map(encodeURIComponent);
|
|
10
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFile as readFileNode } from "node:fs";
|
|
2
2
|
|
|
3
|
-
import { assertAndNormalizeFileUrl } from "
|
|
3
|
+
import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
|
|
4
4
|
|
|
5
5
|
export const readFile = async (value, { as = "buffer" } = {}) => {
|
|
6
6
|
const fileUrl = assertAndNormalizeFileUrl(value);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readdirSync, statSync, readFileSync } from "node:fs";
|
|
2
|
+
import { urlToRelativeUrl } from "@jsenv/urls";
|
|
3
|
+
import { CONTENT_TYPE } from "@jsenv/utils/src/content_type/content_type.js";
|
|
4
|
+
|
|
5
|
+
import { comparePathnames } from "../path_and_url/compare_pathnames.js";
|
|
6
|
+
|
|
7
|
+
export const readFileStructureSync = (directoryUrl) => {
|
|
8
|
+
const fileStructure = {};
|
|
9
|
+
const fileStructureNotOrdered = {};
|
|
10
|
+
const visitDirectory = (url) => {
|
|
11
|
+
try {
|
|
12
|
+
const directoryContent = readdirSync(url);
|
|
13
|
+
directoryContent.forEach((filename) => {
|
|
14
|
+
const contentUrl = new URL(filename, url);
|
|
15
|
+
const stat = statSync(contentUrl);
|
|
16
|
+
if (stat.isDirectory()) {
|
|
17
|
+
visitDirectory(new URL(`${contentUrl}/`));
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const relativeUrl = urlToRelativeUrl(contentUrl, directoryUrl);
|
|
21
|
+
fileStructureNotOrdered[relativeUrl] = readFileContent(contentUrl);
|
|
22
|
+
});
|
|
23
|
+
} catch (e) {
|
|
24
|
+
if (e && e.code === "ENOENT") {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
throw e;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
visitDirectory(new URL(directoryUrl));
|
|
31
|
+
|
|
32
|
+
const relativeUrls = Object.keys(fileStructureNotOrdered);
|
|
33
|
+
relativeUrls.sort(comparePathnames);
|
|
34
|
+
relativeUrls.forEach((relativeUrl) => {
|
|
35
|
+
fileStructure[relativeUrl] = fileStructureNotOrdered[relativeUrl];
|
|
36
|
+
});
|
|
37
|
+
return fileStructure;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const readFileContent = (fileUrl) => {
|
|
41
|
+
const isTextual = CONTENT_TYPE.isTextual(
|
|
42
|
+
CONTENT_TYPE.fromUrlExtension(fileUrl),
|
|
43
|
+
);
|
|
44
|
+
if (isTextual) {
|
|
45
|
+
const content = readFileSync(new URL(fileUrl), "utf8");
|
|
46
|
+
if (process.platform === "win32") {
|
|
47
|
+
// ensure unix line breaks
|
|
48
|
+
return content.replace(/\r\n/g, "\n");
|
|
49
|
+
}
|
|
50
|
+
return content;
|
|
51
|
+
}
|
|
52
|
+
const content = readFileSync(new URL(fileUrl));
|
|
53
|
+
if (content.length === 0) {
|
|
54
|
+
return "";
|
|
55
|
+
}
|
|
56
|
+
return content;
|
|
57
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync as readFileSyncNode } from "node:fs";
|
|
2
2
|
|
|
3
|
-
import { assertAndNormalizeFileUrl } from "
|
|
3
|
+
import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
|
|
4
4
|
|
|
5
5
|
export const readFileSync = (value, { as = "buffer" } = {}) => {
|
|
6
6
|
const fileUrl = assertAndNormalizeFileUrl(value);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
import { readlink } from "node:fs";
|
|
7
7
|
import { isFileSystemPath, fileSystemPathToUrl } from "@jsenv/urls";
|
|
8
8
|
|
|
9
|
-
import { assertAndNormalizeFileUrl } from "
|
|
9
|
+
import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
|
|
10
10
|
|
|
11
11
|
export const readSymbolicLink = (url) => {
|
|
12
12
|
const symbolicLinkUrl = assertAndNormalizeFileUrl(url);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* - symlink documentation on Node.js:
|
|
3
|
+
* https://nodejs.org/docs/latest-v13.x/api/fs.html#fs_fs_symlink_target_path_type_callback
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readlinkSync } from "node:fs";
|
|
7
|
+
import { isFileSystemPath, fileSystemPathToUrl } from "@jsenv/urls";
|
|
8
|
+
|
|
9
|
+
import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
|
|
10
|
+
|
|
11
|
+
export const readSymbolicLinkSync = (url) => {
|
|
12
|
+
const symbolicLinkUrl = assertAndNormalizeFileUrl(url);
|
|
13
|
+
const resolvedPath = readlinkSync(new URL(symbolicLinkUrl));
|
|
14
|
+
return isFileSystemPath(resolvedPath)
|
|
15
|
+
? fileSystemPathToUrl(resolvedPath)
|
|
16
|
+
: resolvedPath.replace(/\\/g, "/"); // replace back slashes with slashes
|
|
17
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { assertAndNormalizeFileUrl } from "
|
|
2
|
-
import { readEntryPermissions } from "./
|
|
3
|
-
import { writeEntryPermissions } from "./
|
|
1
|
+
import { assertAndNormalizeFileUrl } from "../../path_and_url/file_url_validation.js";
|
|
2
|
+
import { readEntryPermissions } from "./read_entry_permissions.js";
|
|
3
|
+
import { writeEntryPermissions } from "./write_entry_permissions.js";
|
|
4
4
|
|
|
5
5
|
export const grantPermissionsOnEntry = async (
|
|
6
6
|
source,
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
import { promises } from "node:fs";
|
|
7
7
|
import { urlToFileSystemPath } from "@jsenv/urls";
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { assertAndNormalizeFileUrl } from "../../path_and_url/file_url_validation.js";
|
|
10
|
+
import { binaryFlagsToPermissions } from "./permissions.js";
|
|
11
11
|
|
|
12
12
|
const { stat } = promises;
|
|
13
13
|
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
import { lstat, stat } from "node:fs";
|
|
7
7
|
import { urlToFileSystemPath } from "@jsenv/urls";
|
|
8
8
|
|
|
9
|
-
import { assertAndNormalizeFileUrl } from "
|
|
10
|
-
import { writeEntryPermissions } from "./
|
|
9
|
+
import { assertAndNormalizeFileUrl } from "../../path_and_url/file_url_validation.js";
|
|
10
|
+
import { writeEntryPermissions } from "./write_entry_permissions.js";
|
|
11
11
|
|
|
12
12
|
const isWindows = process.platform === "win32";
|
|
13
13
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* - stats object documentation on Node.js
|
|
3
|
+
* https://nodejs.org/docs/latest-v13.x/api/fs.html#fs_class_fs_stats
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { lstatSync, statSync } from "node:fs";
|
|
7
|
+
import { urlToFileSystemPath } from "@jsenv/urls";
|
|
8
|
+
|
|
9
|
+
import { assertAndNormalizeFileUrl } from "../../path_and_url/file_url_validation.js";
|
|
10
|
+
import { writeEntryPermissionsSync } from "./write_entry_permissions_sync.js";
|
|
11
|
+
|
|
12
|
+
const isWindows = process.platform === "win32";
|
|
13
|
+
|
|
14
|
+
export const readEntryStatSync = (
|
|
15
|
+
source,
|
|
16
|
+
{ nullIfNotFound = false, followLink = true } = {},
|
|
17
|
+
) => {
|
|
18
|
+
let sourceUrl = assertAndNormalizeFileUrl(source);
|
|
19
|
+
if (sourceUrl.endsWith("/")) sourceUrl = sourceUrl.slice(0, -1);
|
|
20
|
+
|
|
21
|
+
const sourcePath = urlToFileSystemPath(sourceUrl);
|
|
22
|
+
|
|
23
|
+
const handleNotFoundOption = nullIfNotFound
|
|
24
|
+
? {
|
|
25
|
+
handleNotFoundError: () => null,
|
|
26
|
+
}
|
|
27
|
+
: {};
|
|
28
|
+
|
|
29
|
+
return statSyncNaive(sourcePath, {
|
|
30
|
+
followLink,
|
|
31
|
+
...handleNotFoundOption,
|
|
32
|
+
...(isWindows
|
|
33
|
+
? {
|
|
34
|
+
// Windows can EPERM on stat
|
|
35
|
+
handlePermissionDeniedError: (error) => {
|
|
36
|
+
console.error(
|
|
37
|
+
`trying to fix windows EPERM after stats on ${sourcePath}`,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
// unfortunately it means we mutate the permissions
|
|
42
|
+
// without being able to restore them to the previous value
|
|
43
|
+
// (because reading current permission would also throw)
|
|
44
|
+
writeEntryPermissionsSync(sourceUrl, 0o666);
|
|
45
|
+
const stats = statSyncNaive(sourcePath, {
|
|
46
|
+
followLink,
|
|
47
|
+
...handleNotFoundOption,
|
|
48
|
+
// could not fix the permission error, give up and throw original error
|
|
49
|
+
handlePermissionDeniedError: () => {
|
|
50
|
+
console.error(`still got EPERM after stats on ${sourcePath}`);
|
|
51
|
+
throw error;
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
return stats;
|
|
55
|
+
} catch (e) {
|
|
56
|
+
console.error(
|
|
57
|
+
`error while trying to fix windows EPERM after stats on ${sourcePath}: ${e.stack}`,
|
|
58
|
+
);
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
: {}),
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const statSyncNaive = (
|
|
68
|
+
sourcePath,
|
|
69
|
+
{
|
|
70
|
+
followLink,
|
|
71
|
+
handleNotFoundError = null,
|
|
72
|
+
handlePermissionDeniedError = null,
|
|
73
|
+
} = {},
|
|
74
|
+
) => {
|
|
75
|
+
const nodeMethod = followLink ? statSync : lstatSync;
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const stats = nodeMethod(sourcePath);
|
|
79
|
+
return stats;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (handleNotFoundError && error.code === "ENOENT") {
|
|
82
|
+
return handleNotFoundError(error);
|
|
83
|
+
}
|
|
84
|
+
if (
|
|
85
|
+
handlePermissionDeniedError &&
|
|
86
|
+
(error.code === "EPERM" || error.code === "EACCES")
|
|
87
|
+
) {
|
|
88
|
+
return handlePermissionDeniedError(error);
|
|
89
|
+
}
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
};
|
package/src/{writeEntryModificationTime.js → read_write/stat/write_entry_modification_time.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { utimes } from "node:fs";
|
|
2
2
|
|
|
3
|
-
import { assertAndNormalizeFileUrl } from "
|
|
3
|
+
import { assertAndNormalizeFileUrl } from "../../path_and_url/file_url_validation.js";
|
|
4
4
|
|
|
5
5
|
export const writeEntryModificationTime = (source, mtime) => {
|
|
6
6
|
const sourceUrl = assertAndNormalizeFileUrl(source);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { utimesSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
import { assertAndNormalizeFileUrl } from "../../path_and_url/file_url_validation.js";
|
|
4
|
+
|
|
5
|
+
export const writeEntryModificationTimeSync = (source, mtime) => {
|
|
6
|
+
const sourceUrl = assertAndNormalizeFileUrl(source);
|
|
7
|
+
const mtimeValue =
|
|
8
|
+
typeof mtime === "number" ? new Date(Math.floor(mtime)) : mtime;
|
|
9
|
+
// reading atime mutates its value so there is no use case I can think of
|
|
10
|
+
// where we want to modify it
|
|
11
|
+
const atimeValue = mtimeValue;
|
|
12
|
+
|
|
13
|
+
utimesSync(new URL(sourceUrl), atimeValue, mtimeValue);
|
|
14
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { chmod } from "node:fs";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { assertAndNormalizeFileUrl } from "../../path_and_url/file_url_validation.js";
|
|
4
|
+
import { permissionsToBinaryFlags } from "./permissions.js";
|
|
5
5
|
|
|
6
6
|
export const writeEntryPermissions = async (source, permissions) => {
|
|
7
7
|
const sourceUrl = assertAndNormalizeFileUrl(source);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { chmodSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
import { assertAndNormalizeFileUrl } from "../../path_and_url/file_url_validation.js";
|
|
4
|
+
import { permissionsToBinaryFlags } from "./permissions.js";
|
|
5
|
+
|
|
6
|
+
export const writeEntryPermissionsSync = (source, permissions) => {
|
|
7
|
+
const sourceUrl = assertAndNormalizeFileUrl(source);
|
|
8
|
+
|
|
9
|
+
let binaryFlags;
|
|
10
|
+
if (typeof permissions === "object") {
|
|
11
|
+
permissions = {
|
|
12
|
+
owner: {
|
|
13
|
+
read: getPermissionOrComputeDefault("read", "owner", permissions),
|
|
14
|
+
write: getPermissionOrComputeDefault("write", "owner", permissions),
|
|
15
|
+
execute: getPermissionOrComputeDefault("execute", "owner", permissions),
|
|
16
|
+
},
|
|
17
|
+
group: {
|
|
18
|
+
read: getPermissionOrComputeDefault("read", "group", permissions),
|
|
19
|
+
write: getPermissionOrComputeDefault("write", "group", permissions),
|
|
20
|
+
execute: getPermissionOrComputeDefault("execute", "group", permissions),
|
|
21
|
+
},
|
|
22
|
+
others: {
|
|
23
|
+
read: getPermissionOrComputeDefault("read", "others", permissions),
|
|
24
|
+
write: getPermissionOrComputeDefault("write", "others", permissions),
|
|
25
|
+
execute: getPermissionOrComputeDefault(
|
|
26
|
+
"execute",
|
|
27
|
+
"others",
|
|
28
|
+
permissions,
|
|
29
|
+
),
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
binaryFlags = permissionsToBinaryFlags(permissions);
|
|
33
|
+
} else {
|
|
34
|
+
binaryFlags = permissions;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
chmodSync(new URL(sourceUrl), binaryFlags);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const actionLevels = { read: 0, write: 1, execute: 2 };
|
|
41
|
+
const subjectLevels = { others: 0, group: 1, owner: 2 };
|
|
42
|
+
|
|
43
|
+
const getPermissionOrComputeDefault = (action, subject, permissions) => {
|
|
44
|
+
if (subject in permissions) {
|
|
45
|
+
const subjectPermissions = permissions[subject];
|
|
46
|
+
if (action in subjectPermissions) {
|
|
47
|
+
return subjectPermissions[action];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const actionLevel = actionLevels[action];
|
|
51
|
+
const actionFallback = Object.keys(actionLevels).find(
|
|
52
|
+
(actionFallbackCandidate) =>
|
|
53
|
+
actionLevels[actionFallbackCandidate] > actionLevel &&
|
|
54
|
+
actionFallbackCandidate in subjectPermissions,
|
|
55
|
+
);
|
|
56
|
+
if (actionFallback) {
|
|
57
|
+
return subjectPermissions[actionFallback];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const subjectLevel = subjectLevels[subject];
|
|
62
|
+
// do we have a subject with a stronger level (group or owner)
|
|
63
|
+
// where we could read the action permission ?
|
|
64
|
+
const subjectFallback = Object.keys(subjectLevels).find(
|
|
65
|
+
(subjectFallbackCandidate) =>
|
|
66
|
+
subjectLevels[subjectFallbackCandidate] > subjectLevel &&
|
|
67
|
+
subjectFallbackCandidate in permissions,
|
|
68
|
+
);
|
|
69
|
+
if (subjectFallback) {
|
|
70
|
+
const subjectPermissions = permissions[subjectFallback];
|
|
71
|
+
return action in subjectPermissions
|
|
72
|
+
? subjectPermissions[action]
|
|
73
|
+
: getPermissionOrComputeDefault(action, subjectFallback, permissions);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return false;
|
|
77
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { promises } from "node:fs";
|
|
2
2
|
import { urlToFileSystemPath } from "@jsenv/urls";
|
|
3
3
|
|
|
4
|
-
import { assertAndNormalizeDirectoryUrl } from "
|
|
5
|
-
import { statsToType } from "./
|
|
6
|
-
import { readEntryStat } from "./
|
|
4
|
+
import { assertAndNormalizeDirectoryUrl } from "../path_and_url/directory_url_validation.js";
|
|
5
|
+
import { statsToType } from "./stat/stats_to_type.js";
|
|
6
|
+
import { readEntryStat } from "./stat/read_entry_stat.js";
|
|
7
7
|
|
|
8
8
|
// https://nodejs.org/dist/latest-v13.x/docs/api/fs.html#fs_fspromises_mkdir_path_options
|
|
9
9
|
const { mkdir } = promises;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { mkdirSync } from "node:fs";
|
|
2
|
+
import { urlToFileSystemPath } from "@jsenv/urls";
|
|
3
|
+
|
|
4
|
+
import { assertAndNormalizeDirectoryUrl } from "../path_and_url/directory_url_validation.js";
|
|
5
|
+
import { readEntryStatSync } from "./stat/read_entry_stat_sync.js";
|
|
6
|
+
import { statsToType } from "./stat/stats_to_type.js";
|
|
7
|
+
|
|
8
|
+
export const writeDirectorySync = (
|
|
9
|
+
destination,
|
|
10
|
+
{ recursive = true, allowUseless = false } = {},
|
|
11
|
+
) => {
|
|
12
|
+
const destinationUrl = assertAndNormalizeDirectoryUrl(destination);
|
|
13
|
+
const destinationPath = urlToFileSystemPath(destinationUrl);
|
|
14
|
+
|
|
15
|
+
const destinationStats = readEntryStatSync(destinationUrl, {
|
|
16
|
+
nullIfNotFound: true,
|
|
17
|
+
followLink: false,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if (destinationStats) {
|
|
21
|
+
if (destinationStats.isDirectory()) {
|
|
22
|
+
if (allowUseless) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
throw new Error(`directory already exists at ${destinationPath}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const destinationType = statsToType(destinationStats);
|
|
29
|
+
throw new Error(
|
|
30
|
+
`cannot write directory at ${destinationPath} because there is a ${destinationType}`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
mkdirSync(destinationPath, { recursive });
|
|
36
|
+
} catch (error) {
|
|
37
|
+
if (allowUseless && error.code === "EEXIST") {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { writeFile as writeFileNode } from "node:fs";
|
|
2
2
|
|
|
3
|
-
import { assertAndNormalizeFileUrl } from "
|
|
4
|
-
import { ensureParentDirectories } from "./
|
|
3
|
+
import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
|
|
4
|
+
import { ensureParentDirectories } from "./ensure_parent_directories.js";
|
|
5
5
|
|
|
6
6
|
export const writeFile = async (destination, content = "") => {
|
|
7
7
|
const destinationUrl = assertAndNormalizeFileUrl(destination);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ensureEmptyDirectorySync } from "../remove/ensure_empty_directory_sync.js";
|
|
2
|
+
import { writeFileSync } from "./write_file_sync.js";
|
|
3
|
+
|
|
4
|
+
export const writeFileStructureSync = (directoryUrl, fileStructure) => {
|
|
5
|
+
ensureEmptyDirectorySync(directoryUrl);
|
|
6
|
+
if (fileStructure) {
|
|
7
|
+
Object.keys(fileStructure).forEach((relativeUrl) => {
|
|
8
|
+
const fileUrl = new URL(relativeUrl, directoryUrl);
|
|
9
|
+
const fileContent = fileStructure[relativeUrl];
|
|
10
|
+
writeFileSync(fileUrl, fileContent);
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { writeFileSync as writeFileSyncNode, mkdirSync } from "node:fs";
|
|
2
2
|
|
|
3
|
-
import { assertAndNormalizeFileUrl } from "
|
|
3
|
+
import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
|
|
4
4
|
|
|
5
5
|
export const writeFileSync = (destination, content = "") => {
|
|
6
6
|
const destinationUrl = assertAndNormalizeFileUrl(destination);
|
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
isFileSystemPath,
|
|
7
7
|
} from "@jsenv/urls";
|
|
8
8
|
|
|
9
|
-
import { assertAndNormalizeFileUrl } from "
|
|
10
|
-
import {
|
|
11
|
-
import { readEntryStat } from "./
|
|
12
|
-
import { readSymbolicLink } from "./
|
|
13
|
-
import {
|
|
9
|
+
import { assertAndNormalizeFileUrl } from "../path_and_url/file_url_validation.js";
|
|
10
|
+
import { removeEntry } from "../remove/remove_entry.js";
|
|
11
|
+
import { readEntryStat } from "./stat/read_entry_stat.js";
|
|
12
|
+
import { readSymbolicLink } from "./read_symbolic_link.js";
|
|
13
|
+
import { ensureParentDirectories } from "./ensure_parent_directories.js";
|
|
14
14
|
|
|
15
15
|
// https://nodejs.org/dist/latest-v13.x/docs/api/fs.html#fs_fspromises_symlink_target_path_type
|
|
16
16
|
const { symlink } = promises;
|