@jsenv/filesystem 2.0.0 → 2.3.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 +29 -38
- package/dist/commonjs/jsenv_filesystem.cjs +174 -26
- package/dist/commonjs/jsenv_filesystem.cjs.map +50 -36
- package/main.js +2 -0
- package/package.json +11 -10
- package/src/assertAndNormalizeDirectoryUrl.js +6 -2
- package/src/assertDirectoryPresence.js +3 -1
- package/src/assertFilePresence.js +5 -1
- package/src/collectDirectoryMatchReport.js +19 -4
- package/src/collectFiles.js +13 -3
- package/src/copyDirectoryContent.js +81 -0
- package/src/copyFileSystemNode.js +27 -8
- package/src/ensureEmptyDirectory.js +4 -1
- package/src/ensureParentDirectories.js +4 -1
- package/src/ensureWindowsDriveLetter.js +6 -2
- package/src/internal/getCommonPathname.js +4 -1
- package/src/internal/replaceBackSlashesWithSlashes.js +2 -1
- package/src/isFileSystemPath.js +3 -1
- package/src/listFilesMatching.js +34 -0
- package/src/moveDirectoryContent.js +15 -4
- package/src/moveFileSystemNode.js +11 -3
- package/src/readDirectory.js +8 -2
- package/src/readFileSystemNodeStat.js +8 -2
- package/src/registerDirectoryLifecycle.js +52 -27
- package/src/registerFileLifecycle.js +32 -10
- package/src/removeFileSystemNode.js +18 -7
- package/src/testFileSystemNodePermissions.js +6 -1
- package/src/urlToBasename.js +2 -1
- package/src/urlToFilename.js +3 -1
- package/src/urlToParentUrl.js +4 -1
- package/src/urlToPathname.js +3 -1
- package/src/urlToRelativeUrl.js +6 -2
- package/src/writeFileSystemNodeModificationTime.js +2 -1
- package/src/writeFileSystemNodePermissions.js +5 -1
- package/src/writeSymbolicLink.js +82 -26
package/main.js
CHANGED
|
@@ -22,9 +22,11 @@ export { ensureEmptyDirectory } from "./src/ensureEmptyDirectory.js"
|
|
|
22
22
|
export { ensureParentDirectories } from "./src/ensureParentDirectories.js"
|
|
23
23
|
export { ensureWindowsDriveLetter } from "./src/ensureWindowsDriveLetter.js"
|
|
24
24
|
export { copyFileSystemNode } from "./src/copyFileSystemNode.js"
|
|
25
|
+
export { copyDirectoryContent } from "./src/copyDirectoryContent.js"
|
|
25
26
|
export { fileSystemPathToUrl } from "./src/fileSystemPathToUrl.js"
|
|
26
27
|
export { grantPermissionsOnFileSystemNode } from "./src/grantPermissionsOnFileSystemNode.js"
|
|
27
28
|
export { isFileSystemPath } from "./src/isFileSystemPath.js"
|
|
29
|
+
export { listFilesMatching } from "./src/listFilesMatching.js"
|
|
28
30
|
export { memoize } from "./src/memoize.js"
|
|
29
31
|
export { moveDirectoryContent } from "./src/moveDirectoryContent.js"
|
|
30
32
|
export { moveFileSystemNode } from "./src/moveFileSystemNode.js"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/filesystem",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.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": ">=14.17.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": {
|
|
@@ -48,21 +47,23 @@
|
|
|
48
47
|
},
|
|
49
48
|
"dependencies": {
|
|
50
49
|
"@jsenv/cancellation": "3.0.0",
|
|
51
|
-
"@jsenv/url-meta": "6.0.
|
|
50
|
+
"@jsenv/url-meta": "6.0.1"
|
|
52
51
|
},
|
|
53
52
|
"devDependencies": {
|
|
53
|
+
"@babel/core": "7.15.5",
|
|
54
|
+
"@babel/eslint-parser": "7.15.7",
|
|
54
55
|
"@jsenv/assert": "2.2.7",
|
|
55
56
|
"@jsenv/codecov-upload": "3.5.0",
|
|
56
|
-
"@jsenv/core": "
|
|
57
|
-
"@jsenv/eslint-config": "
|
|
57
|
+
"@jsenv/core": "20.3.0",
|
|
58
|
+
"@jsenv/eslint-config": "16.0.3",
|
|
58
59
|
"@jsenv/github-release-package": "1.2.3",
|
|
59
|
-
"@jsenv/importmap-eslint-resolver": "5.1.
|
|
60
|
-
"@jsenv/importmap-node-module": "
|
|
60
|
+
"@jsenv/importmap-eslint-resolver": "5.1.2",
|
|
61
|
+
"@jsenv/importmap-node-module": "2.2.1",
|
|
61
62
|
"@jsenv/package-publish": "1.6.2",
|
|
62
63
|
"@jsenv/performance-impact": "1.7.0",
|
|
63
64
|
"@jsenv/prettier-check-project": "5.6.1",
|
|
64
65
|
"eslint": "7.32.0",
|
|
65
|
-
"eslint-plugin-import": "2.24.
|
|
66
|
-
"prettier": "2.
|
|
66
|
+
"eslint-plugin-import": "2.24.2",
|
|
67
|
+
"prettier": "2.4.1"
|
|
67
68
|
}
|
|
68
69
|
}
|
|
@@ -14,11 +14,15 @@ export const assertAndNormalizeDirectoryUrl = (value) => {
|
|
|
14
14
|
try {
|
|
15
15
|
urlString = String(new URL(value))
|
|
16
16
|
} catch (e) {
|
|
17
|
-
throw new TypeError(
|
|
17
|
+
throw new TypeError(
|
|
18
|
+
`directoryUrl must be a valid url, received ${value}`,
|
|
19
|
+
)
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
} else {
|
|
21
|
-
throw new TypeError(
|
|
23
|
+
throw new TypeError(
|
|
24
|
+
`directoryUrl must be a string or an url, received ${value}`,
|
|
25
|
+
)
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
if (!urlString.startsWith("file://")) {
|
|
@@ -15,7 +15,9 @@ export const assertDirectoryPresence = async (source) => {
|
|
|
15
15
|
}
|
|
16
16
|
if (!sourceStats.isDirectory()) {
|
|
17
17
|
throw new Error(
|
|
18
|
-
`directory expected at ${sourcePath} and found ${statsToType(
|
|
18
|
+
`directory expected at ${sourcePath} and found ${statsToType(
|
|
19
|
+
sourceStats,
|
|
20
|
+
)} instead`,
|
|
19
21
|
)
|
|
20
22
|
}
|
|
21
23
|
}
|
|
@@ -14,6 +14,10 @@ export const assertFilePresence = async (source) => {
|
|
|
14
14
|
throw new Error(`file not found at ${sourcePath}`)
|
|
15
15
|
}
|
|
16
16
|
if (!sourceStats.isFile()) {
|
|
17
|
-
throw new Error(
|
|
17
|
+
throw new Error(
|
|
18
|
+
`file expected at ${sourcePath} and found ${statsToType(
|
|
19
|
+
sourceStats,
|
|
20
|
+
)} instead`,
|
|
21
|
+
)
|
|
18
22
|
}
|
|
19
23
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { createCancellationToken, createOperation } from "@jsenv/cancellation"
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
normalizeStructuredMetaMap,
|
|
4
|
+
urlCanContainsMetaMatching,
|
|
5
|
+
urlToMeta,
|
|
6
|
+
} from "@jsenv/url-meta"
|
|
3
7
|
|
|
4
8
|
import { ensureUrlTrailingSlash } from "./internal/ensureUrlTrailingSlash.js"
|
|
5
9
|
import { assertAndNormalizeDirectoryUrl } from "./assertAndNormalizeDirectoryUrl.js"
|
|
@@ -35,7 +39,10 @@ export const collectDirectoryMatchReport = async ({
|
|
|
35
39
|
await Promise.all(
|
|
36
40
|
directoryItems.map(async (directoryItem) => {
|
|
37
41
|
const directoryChildNodeUrl = `${directoryUrl}${directoryItem}`
|
|
38
|
-
const relativeUrl = urlToRelativeUrl(
|
|
42
|
+
const relativeUrl = urlToRelativeUrl(
|
|
43
|
+
directoryChildNodeUrl,
|
|
44
|
+
rootDirectoryUrl,
|
|
45
|
+
)
|
|
39
46
|
|
|
40
47
|
const directoryChildNodeStats = await createOperation({
|
|
41
48
|
cancellationToken,
|
|
@@ -79,11 +86,19 @@ export const collectDirectoryMatchReport = async ({
|
|
|
79
86
|
structuredMetaMap: structuredMetaMapNormalized,
|
|
80
87
|
})
|
|
81
88
|
if (!predicate(meta)) {
|
|
82
|
-
ignoredArray.push({
|
|
89
|
+
ignoredArray.push({
|
|
90
|
+
relativeUrl,
|
|
91
|
+
meta,
|
|
92
|
+
fileStats: directoryChildNodeStats,
|
|
93
|
+
})
|
|
83
94
|
return
|
|
84
95
|
}
|
|
85
96
|
|
|
86
|
-
matchingArray.push({
|
|
97
|
+
matchingArray.push({
|
|
98
|
+
relativeUrl,
|
|
99
|
+
meta,
|
|
100
|
+
fileStats: directoryChildNodeStats,
|
|
101
|
+
})
|
|
87
102
|
return
|
|
88
103
|
}
|
|
89
104
|
}),
|
package/src/collectFiles.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { createCancellationToken, createOperation } from "@jsenv/cancellation"
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
normalizeStructuredMetaMap,
|
|
4
|
+
urlCanContainsMetaMatching,
|
|
5
|
+
urlToMeta,
|
|
6
|
+
} from "@jsenv/url-meta"
|
|
3
7
|
|
|
4
8
|
import { assertAndNormalizeDirectoryUrl } from "./assertAndNormalizeDirectoryUrl.js"
|
|
5
9
|
import { readDirectory } from "./readDirectory.js"
|
|
@@ -19,7 +23,9 @@ export const collectFiles = async ({
|
|
|
19
23
|
throw new TypeError(`predicate must be a function, got ${predicate}`)
|
|
20
24
|
}
|
|
21
25
|
if (typeof matchingFileOperation !== "function") {
|
|
22
|
-
throw new TypeError(
|
|
26
|
+
throw new TypeError(
|
|
27
|
+
`matchingFileOperation must be a function, got ${matchingFileOperation}`,
|
|
28
|
+
)
|
|
23
29
|
}
|
|
24
30
|
const structuredMetaMapNormalized = normalizeStructuredMetaMap(
|
|
25
31
|
structuredMetaMap,
|
|
@@ -75,7 +81,10 @@ export const collectFiles = async ({
|
|
|
75
81
|
})
|
|
76
82
|
if (!predicate(meta)) return
|
|
77
83
|
|
|
78
|
-
const relativeUrl = urlToRelativeUrl(
|
|
84
|
+
const relativeUrl = urlToRelativeUrl(
|
|
85
|
+
directoryChildNodeUrl,
|
|
86
|
+
rootDirectoryUrl,
|
|
87
|
+
)
|
|
79
88
|
const operationResult = await createOperation({
|
|
80
89
|
cancellationToken,
|
|
81
90
|
start: () =>
|
|
@@ -87,6 +96,7 @@ export const collectFiles = async ({
|
|
|
87
96
|
}),
|
|
88
97
|
})
|
|
89
98
|
matchingFileResultArray.push({
|
|
99
|
+
url: new URL(relativeUrl, directoryUrl).href,
|
|
90
100
|
relativeUrl,
|
|
91
101
|
meta,
|
|
92
102
|
fileStats: directoryChildNodeStats,
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { assertAndNormalizeDirectoryUrl } from "./assertAndNormalizeDirectoryUrl.js"
|
|
2
|
+
import { readFileSystemNodeStat } from "./readFileSystemNodeStat.js"
|
|
3
|
+
import { urlToFileSystemPath } from "./urlToFileSystemPath.js"
|
|
4
|
+
import { readSymbolicLink } from "./readSymbolicLink.js"
|
|
5
|
+
import { resolveUrl } from "./resolveUrl.js"
|
|
6
|
+
import { readDirectory } from "./readDirectory.js"
|
|
7
|
+
import { copyFileSystemNode } from "./copyFileSystemNode.js"
|
|
8
|
+
|
|
9
|
+
import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js"
|
|
10
|
+
import { statsToType } from "./internal/statsToType.js"
|
|
11
|
+
|
|
12
|
+
export const copyDirectoryContent = async ({
|
|
13
|
+
from,
|
|
14
|
+
to,
|
|
15
|
+
overwrite,
|
|
16
|
+
followLink = true,
|
|
17
|
+
} = {}) => {
|
|
18
|
+
const fromUrl = assertAndNormalizeDirectoryUrl(from)
|
|
19
|
+
const fromPath = urlToFileSystemPath(fromUrl)
|
|
20
|
+
let toUrl = assertAndNormalizeDirectoryUrl(to)
|
|
21
|
+
let toPath = urlToFileSystemPath(toUrl)
|
|
22
|
+
|
|
23
|
+
const sourceStats = await readFileSystemNodeStat(fromUrl, {
|
|
24
|
+
nullIfNotFound: true,
|
|
25
|
+
followLink: false,
|
|
26
|
+
})
|
|
27
|
+
if (!sourceStats) {
|
|
28
|
+
throw new Error(`no directory to copy content from at ${fromPath}`)
|
|
29
|
+
}
|
|
30
|
+
if (!sourceStats.isDirectory()) {
|
|
31
|
+
const sourceType = statsToType(sourceStats)
|
|
32
|
+
throw new Error(
|
|
33
|
+
`found a ${sourceType} instead of a directory at ${fromPath}`,
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let destinationStats = await readFileSystemNodeStat(toUrl, {
|
|
38
|
+
nullIfNotFound: true,
|
|
39
|
+
// we force false here but in fact we will follow the destination link
|
|
40
|
+
// to know where we will actually move and detect useless move overrite etc..
|
|
41
|
+
followLink: false,
|
|
42
|
+
})
|
|
43
|
+
if (followLink && destinationStats && destinationStats.isSymbolicLink()) {
|
|
44
|
+
const linkTarget = await readSymbolicLink(toUrl)
|
|
45
|
+
toUrl = resolveUrl(linkTarget, toUrl)
|
|
46
|
+
toPath = urlToFileSystemPath(toUrl)
|
|
47
|
+
destinationStats = await readFileSystemNodeStat(toUrl, {
|
|
48
|
+
nullIfNotFound: true,
|
|
49
|
+
})
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (destinationStats === null) {
|
|
53
|
+
throw new Error(`no directory to copy content into at ${toPath}`)
|
|
54
|
+
}
|
|
55
|
+
if (!destinationStats.isDirectory()) {
|
|
56
|
+
const destinationType = statsToType(destinationStats)
|
|
57
|
+
throw new Error(
|
|
58
|
+
`destination leads to a ${destinationType} instead of a directory at ${toPath}`,
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (urlTargetsSameFileSystemPath(fromUrl, toUrl)) {
|
|
63
|
+
throw new Error(
|
|
64
|
+
`cannot copy directory content, source and destination are the same (${fromPath})`,
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const directoryEntries = await readDirectory(fromUrl)
|
|
69
|
+
await Promise.all(
|
|
70
|
+
directoryEntries.map(async (directoryEntry) => {
|
|
71
|
+
const from = resolveUrl(directoryEntry, fromUrl)
|
|
72
|
+
const to = resolveUrl(directoryEntry, toUrl)
|
|
73
|
+
await copyFileSystemNode({
|
|
74
|
+
from,
|
|
75
|
+
to,
|
|
76
|
+
overwrite,
|
|
77
|
+
followLink,
|
|
78
|
+
})
|
|
79
|
+
}),
|
|
80
|
+
)
|
|
81
|
+
}
|
|
@@ -54,14 +54,18 @@ export const copyFileSystemNode = async ({
|
|
|
54
54
|
const linkTarget = await readSymbolicLink(toUrl)
|
|
55
55
|
toUrl = resolveUrl(linkTarget, toUrl)
|
|
56
56
|
toPath = urlToFileSystemPath(toUrl)
|
|
57
|
-
destinationStats = await readFileSystemNodeStat(toUrl, {
|
|
57
|
+
destinationStats = await readFileSystemNodeStat(toUrl, {
|
|
58
|
+
nullIfNotFound: true,
|
|
59
|
+
})
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
if (urlTargetsSameFileSystemPath(fromUrl, toUrl)) {
|
|
61
63
|
if (allowUseless) {
|
|
62
64
|
return
|
|
63
65
|
}
|
|
64
|
-
throw new Error(
|
|
66
|
+
throw new Error(
|
|
67
|
+
`cannot copy ${fromPath} because destination and source are the same`,
|
|
68
|
+
)
|
|
65
69
|
}
|
|
66
70
|
|
|
67
71
|
if (destinationStats) {
|
|
@@ -103,16 +107,23 @@ export const copyFileSystemNode = async ({
|
|
|
103
107
|
const fileRelativeUrl = urlToRelativeUrl(fileUrl, fromUrl)
|
|
104
108
|
const fileCopyUrl = resolveUrl(fileRelativeUrl, toUrl)
|
|
105
109
|
|
|
106
|
-
await copyFileContentNaive(
|
|
110
|
+
await copyFileContentNaive(
|
|
111
|
+
urlToFileSystemPath(fileUrl),
|
|
112
|
+
urlToFileSystemPath(fileCopyUrl),
|
|
113
|
+
)
|
|
107
114
|
await copyStats(fileCopyUrl, fileStats)
|
|
108
115
|
}
|
|
109
116
|
|
|
110
117
|
const visitSymbolicLink = async (symbolicLinkUrl) => {
|
|
111
118
|
const symbolicLinkRelativeUrl = urlToRelativeUrl(symbolicLinkUrl, fromUrl)
|
|
112
119
|
const symbolicLinkTarget = await readSymbolicLink(symbolicLinkUrl)
|
|
113
|
-
const symbolicLinkTargetUrl = resolveUrl(
|
|
120
|
+
const symbolicLinkTargetUrl = resolveUrl(
|
|
121
|
+
symbolicLinkTarget,
|
|
122
|
+
symbolicLinkUrl,
|
|
123
|
+
)
|
|
114
124
|
const linkIsRelative =
|
|
115
|
-
symbolicLinkTarget.startsWith("./") ||
|
|
125
|
+
symbolicLinkTarget.startsWith("./") ||
|
|
126
|
+
symbolicLinkTarget.startsWith("../")
|
|
116
127
|
|
|
117
128
|
let symbolicLinkCopyTarget
|
|
118
129
|
if (symbolicLinkTargetUrl === fromUrl) {
|
|
@@ -120,7 +131,10 @@ export const copyFileSystemNode = async ({
|
|
|
120
131
|
} else if (urlIsInsideOf(symbolicLinkTargetUrl, fromUrl)) {
|
|
121
132
|
// symbolic link targets something inside the directory we want to copy
|
|
122
133
|
// reflects it inside the copied directory structure
|
|
123
|
-
const linkCopyTargetRelative = urlToRelativeUrl(
|
|
134
|
+
const linkCopyTargetRelative = urlToRelativeUrl(
|
|
135
|
+
symbolicLinkTargetUrl,
|
|
136
|
+
fromUrl,
|
|
137
|
+
)
|
|
124
138
|
symbolicLinkCopyTarget = linkIsRelative
|
|
125
139
|
? `./${linkCopyTargetRelative}`
|
|
126
140
|
: resolveUrl(linkCopyTargetRelative, toUrl)
|
|
@@ -150,7 +164,10 @@ export const copyFileSystemNode = async ({
|
|
|
150
164
|
if (preservePermissions || preserveMtime) {
|
|
151
165
|
const { mode, mtimeMs } = stats
|
|
152
166
|
if (preservePermissions) {
|
|
153
|
-
await writeFileSystemNodePermissions(
|
|
167
|
+
await writeFileSystemNodePermissions(
|
|
168
|
+
toUrl,
|
|
169
|
+
binaryFlagsToPermissions(mode),
|
|
170
|
+
)
|
|
154
171
|
}
|
|
155
172
|
if (preserveMtime) {
|
|
156
173
|
await writeFileSystemNodeModificationTime(toUrl, mtimeMs)
|
|
@@ -172,7 +189,9 @@ export const copyFileSystemNode = async ({
|
|
|
172
189
|
await Promise.all(
|
|
173
190
|
names.map(async (name) => {
|
|
174
191
|
const fileSystemNodeUrl = resolveUrl(name, directoryUrl)
|
|
175
|
-
const stats = await readFileSystemNodeStat(fileSystemNodeUrl, {
|
|
192
|
+
const stats = await readFileSystemNodeStat(fileSystemNodeUrl, {
|
|
193
|
+
followLink: false,
|
|
194
|
+
})
|
|
176
195
|
await visit(fileSystemNodeUrl, stats)
|
|
177
196
|
}),
|
|
178
197
|
)
|
|
@@ -6,7 +6,10 @@ import { readFileSystemNodeStat } from "./readFileSystemNodeStat.js"
|
|
|
6
6
|
import { removeFileSystemNode } from "./removeFileSystemNode.js"
|
|
7
7
|
|
|
8
8
|
export const ensureEmptyDirectory = async (source) => {
|
|
9
|
-
const stats = await readFileSystemNodeStat(source, {
|
|
9
|
+
const stats = await readFileSystemNodeStat(source, {
|
|
10
|
+
nullIfNotFound: true,
|
|
11
|
+
followLink: false,
|
|
12
|
+
})
|
|
10
13
|
if (stats === null) {
|
|
11
14
|
// if there is nothing, create a directory
|
|
12
15
|
return writeDirectory(source, { allowUseless: true })
|
|
@@ -8,5 +8,8 @@ export const ensureParentDirectories = async (destination) => {
|
|
|
8
8
|
const destinationPath = urlToFileSystemPath(destinationUrl)
|
|
9
9
|
const destinationParentPath = dirname(destinationPath)
|
|
10
10
|
|
|
11
|
-
return writeDirectory(destinationParentPath, {
|
|
11
|
+
return writeDirectory(destinationParentPath, {
|
|
12
|
+
recursive: true,
|
|
13
|
+
allowUseless: true,
|
|
14
|
+
})
|
|
12
15
|
}
|
|
@@ -45,8 +45,12 @@ export const ensureWindowsDriveLetter = (url, baseUrl) => {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
// drive letter was lost, restore it
|
|
48
|
-
const baseUrlOrFallback = baseUrl.startsWith("file://")
|
|
49
|
-
|
|
48
|
+
const baseUrlOrFallback = baseUrl.startsWith("file://")
|
|
49
|
+
? baseUrl
|
|
50
|
+
: baseUrlFallback
|
|
51
|
+
const driveLetter = extractDriveLetter(
|
|
52
|
+
baseUrlOrFallback.slice("file://".length),
|
|
53
|
+
)
|
|
50
54
|
if (!driveLetter) {
|
|
51
55
|
throw new Error(
|
|
52
56
|
`drive letter expected on baseUrl but got ${baseUrl} to ensure windows drive letter on ${url}`,
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export const getCommonPathname = (pathname, otherPathname) => {
|
|
2
|
-
const firstDifferentCharacterIndex = findFirstDifferentCharacterIndex(
|
|
2
|
+
const firstDifferentCharacterIndex = findFirstDifferentCharacterIndex(
|
|
3
|
+
pathname,
|
|
4
|
+
otherPathname,
|
|
5
|
+
)
|
|
3
6
|
|
|
4
7
|
// pathname and otherpathname are exactly the same
|
|
5
8
|
if (firstDifferentCharacterIndex === -1) {
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export const replaceBackSlashesWithSlashes = (string) =>
|
|
1
|
+
export const replaceBackSlashesWithSlashes = (string) =>
|
|
2
|
+
string.replace(/\\/g, "/")
|
package/src/isFileSystemPath.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export const isFileSystemPath = (value) => {
|
|
2
2
|
if (typeof value !== "string") {
|
|
3
|
-
throw new TypeError(
|
|
3
|
+
throw new TypeError(
|
|
4
|
+
`isFileSystemPath first arg must be a string, got ${value}`,
|
|
5
|
+
)
|
|
4
6
|
}
|
|
5
7
|
|
|
6
8
|
if (value[0] === "/") {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { collectFiles } from "./collectFiles.js"
|
|
2
|
+
|
|
3
|
+
export const listFilesMatching = async (
|
|
4
|
+
patterns,
|
|
5
|
+
directoryUrl = process.cwd(),
|
|
6
|
+
) => {
|
|
7
|
+
if (typeof patterns === "string") {
|
|
8
|
+
patterns = [patterns]
|
|
9
|
+
} else if (Array.isArray(patterns)) {
|
|
10
|
+
throw new TypeError(
|
|
11
|
+
`patterns must be a string or an array of strings, got ${patterns}`,
|
|
12
|
+
)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const patternMap = {}
|
|
16
|
+
patterns.forEach((pattern, index) => {
|
|
17
|
+
if (typeof pattern !== "string") {
|
|
18
|
+
throw new TypeError(
|
|
19
|
+
`every pattern must be a string, found ${pattern} at index ${index}`,
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
patternMap[pattern] = true
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const fileDatas = await collectFiles({
|
|
26
|
+
directoryUrl,
|
|
27
|
+
structuredMetaMap: {
|
|
28
|
+
matches: patternMap,
|
|
29
|
+
},
|
|
30
|
+
predicate: ({ matches }) => matches,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
return fileDatas.map(({ url }) => url)
|
|
34
|
+
}
|
|
@@ -9,7 +9,12 @@ import { moveFileSystemNode } from "./moveFileSystemNode.js"
|
|
|
9
9
|
import { urlTargetsSameFileSystemPath } from "./internal/urlTargetsSameFileSystemPath.js"
|
|
10
10
|
import { statsToType } from "./internal/statsToType.js"
|
|
11
11
|
|
|
12
|
-
export const moveDirectoryContent = async ({
|
|
12
|
+
export const moveDirectoryContent = async ({
|
|
13
|
+
from,
|
|
14
|
+
to,
|
|
15
|
+
overwrite,
|
|
16
|
+
followLink = true,
|
|
17
|
+
} = {}) => {
|
|
13
18
|
const fromUrl = assertAndNormalizeDirectoryUrl(from)
|
|
14
19
|
const fromPath = urlToFileSystemPath(fromUrl)
|
|
15
20
|
let toUrl = assertAndNormalizeDirectoryUrl(to)
|
|
@@ -24,7 +29,9 @@ export const moveDirectoryContent = async ({ from, to, overwrite, followLink = t
|
|
|
24
29
|
}
|
|
25
30
|
if (!sourceStats.isDirectory()) {
|
|
26
31
|
const sourceType = statsToType(sourceStats)
|
|
27
|
-
throw new Error(
|
|
32
|
+
throw new Error(
|
|
33
|
+
`found a ${sourceType} instead of a directory at ${fromPath}`,
|
|
34
|
+
)
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
let destinationStats = await readFileSystemNodeStat(toUrl, {
|
|
@@ -37,7 +44,9 @@ export const moveDirectoryContent = async ({ from, to, overwrite, followLink = t
|
|
|
37
44
|
const linkTarget = await readSymbolicLink(toUrl)
|
|
38
45
|
toUrl = resolveUrl(linkTarget, toUrl)
|
|
39
46
|
toPath = urlToFileSystemPath(toUrl)
|
|
40
|
-
destinationStats = await readFileSystemNodeStat(toUrl, {
|
|
47
|
+
destinationStats = await readFileSystemNodeStat(toUrl, {
|
|
48
|
+
nullIfNotFound: true,
|
|
49
|
+
})
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
if (destinationStats === null) {
|
|
@@ -45,7 +54,9 @@ export const moveDirectoryContent = async ({ from, to, overwrite, followLink = t
|
|
|
45
54
|
}
|
|
46
55
|
if (!destinationStats.isDirectory()) {
|
|
47
56
|
const destinationType = statsToType(destinationStats)
|
|
48
|
-
throw new Error(
|
|
57
|
+
throw new Error(
|
|
58
|
+
`destination leads to a ${destinationType} instead of a directory at ${toPath}`,
|
|
59
|
+
)
|
|
49
60
|
}
|
|
50
61
|
|
|
51
62
|
if (urlTargetsSameFileSystemPath(fromUrl, toUrl)) {
|
|
@@ -43,14 +43,18 @@ export const moveFileSystemNode = async ({
|
|
|
43
43
|
const linkTarget = await readSymbolicLink(toUrl)
|
|
44
44
|
toUrl = resolveUrl(linkTarget, toUrl)
|
|
45
45
|
toPath = urlToFileSystemPath(toUrl)
|
|
46
|
-
destinationStats = await readFileSystemNodeStat(toUrl, {
|
|
46
|
+
destinationStats = await readFileSystemNodeStat(toUrl, {
|
|
47
|
+
nullIfNotFound: true,
|
|
48
|
+
})
|
|
47
49
|
}
|
|
48
50
|
|
|
49
51
|
if (urlTargetsSameFileSystemPath(fromUrl, toUrl)) {
|
|
50
52
|
if (allowUseless) {
|
|
51
53
|
return
|
|
52
54
|
}
|
|
53
|
-
throw new Error(
|
|
55
|
+
throw new Error(
|
|
56
|
+
`no move needed for ${fromPath} because destination and source are the same`,
|
|
57
|
+
)
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
if (destinationStats) {
|
|
@@ -86,7 +90,11 @@ export const moveFileSystemNode = async ({
|
|
|
86
90
|
})
|
|
87
91
|
}
|
|
88
92
|
|
|
89
|
-
const moveNaive = (
|
|
93
|
+
const moveNaive = (
|
|
94
|
+
fromPath,
|
|
95
|
+
destinationPath,
|
|
96
|
+
{ handleCrossDeviceError = null } = {},
|
|
97
|
+
) => {
|
|
90
98
|
return new Promise((resolve, reject) => {
|
|
91
99
|
rename(fromPath, destinationPath, (error) => {
|
|
92
100
|
if (error) {
|
package/src/readDirectory.js
CHANGED
|
@@ -30,12 +30,18 @@ export const readDirectory = async (url, { emfileMaxWait = 1000 } = {}) => {
|
|
|
30
30
|
return attempt()
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const readdirNaive = (
|
|
33
|
+
const readdirNaive = (
|
|
34
|
+
directoryPath,
|
|
35
|
+
{ handleTooManyFilesOpenedError = null } = {},
|
|
36
|
+
) => {
|
|
34
37
|
return new Promise((resolve, reject) => {
|
|
35
38
|
readdir(directoryPath, (error, names) => {
|
|
36
39
|
if (error) {
|
|
37
40
|
// https://nodejs.org/dist/latest-v13.x/docs/api/errors.html#errors_common_system_errors
|
|
38
|
-
if (
|
|
41
|
+
if (
|
|
42
|
+
handleTooManyFilesOpenedError &&
|
|
43
|
+
(error.code === "EMFILE" || error.code === "ENFILE")
|
|
44
|
+
) {
|
|
39
45
|
resolve(handleTooManyFilesOpenedError(error))
|
|
40
46
|
} else {
|
|
41
47
|
reject(error)
|
|
@@ -33,7 +33,9 @@ export const readFileSystemNodeStat = async (
|
|
|
33
33
|
? {
|
|
34
34
|
// Windows can EPERM on stat
|
|
35
35
|
handlePermissionDeniedError: async (error) => {
|
|
36
|
-
console.error(
|
|
36
|
+
console.error(
|
|
37
|
+
`trying to fix windows EPERM after stats on ${sourcePath}`,
|
|
38
|
+
)
|
|
37
39
|
|
|
38
40
|
try {
|
|
39
41
|
// unfortunately it means we mutate the permissions
|
|
@@ -64,7 +66,11 @@ export const readFileSystemNodeStat = async (
|
|
|
64
66
|
|
|
65
67
|
const readStat = (
|
|
66
68
|
sourcePath,
|
|
67
|
-
{
|
|
69
|
+
{
|
|
70
|
+
followLink,
|
|
71
|
+
handleNotFoundError = null,
|
|
72
|
+
handlePermissionDeniedError = null,
|
|
73
|
+
} = {},
|
|
68
74
|
) => {
|
|
69
75
|
const nodeMethod = followLink ? stat : lstat
|
|
70
76
|
|