pwd-fs 3.3.5 → 3.5.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/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/powered-file-system/append.d.ts +9 -6
- package/dist/powered-file-system/append.js +4 -4
- package/dist/powered-file-system/chmod.d.ts +4 -4
- package/dist/powered-file-system/chmod.js +9 -10
- package/dist/powered-file-system/chown.d.ts +7 -4
- package/dist/powered-file-system/chown.js +18 -8
- package/dist/powered-file-system/copy.d.ts +10 -5
- package/dist/powered-file-system/copy.js +13 -13
- package/dist/powered-file-system/copy.test.js +46 -0
- package/dist/powered-file-system/empty-dir.d.ts +7 -0
- package/dist/powered-file-system/empty-dir.js +27 -0
- package/dist/powered-file-system/empty-dir.test.d.ts +1 -0
- package/dist/powered-file-system/empty-dir.test.js +61 -0
- package/dist/powered-file-system/mkdir.d.ts +9 -6
- package/dist/powered-file-system/mkdir.js +9 -10
- package/dist/powered-file-system/read.d.ts +10 -6
- package/dist/powered-file-system/read.js +9 -5
- package/dist/powered-file-system/readdir.d.ts +11 -6
- package/dist/powered-file-system/readdir.js +8 -6
- package/dist/powered-file-system/readlink.d.ts +13 -0
- package/dist/powered-file-system/readlink.js +28 -0
- package/dist/powered-file-system/readlink.test.d.ts +1 -0
- package/dist/powered-file-system/readlink.test.js +44 -0
- package/dist/powered-file-system/realpath.d.ts +13 -0
- package/dist/powered-file-system/realpath.js +28 -0
- package/dist/powered-file-system/realpath.test.d.ts +1 -0
- package/dist/powered-file-system/realpath.test.js +44 -0
- package/dist/powered-file-system/remove.d.ts +5 -5
- package/dist/powered-file-system/remove.js +11 -20
- package/dist/powered-file-system/rename.d.ts +5 -5
- package/dist/powered-file-system/rename.js +11 -7
- package/dist/powered-file-system/stat.d.ts +4 -4
- package/dist/powered-file-system/stat.js +8 -6
- package/dist/powered-file-system/symlink.d.ts +4 -4
- package/dist/powered-file-system/symlink.js +11 -4
- package/dist/powered-file-system/test.d.ts +10 -5
- package/dist/powered-file-system/test.js +8 -5
- package/dist/powered-file-system/write.d.ts +10 -7
- package/dist/powered-file-system/write.js +9 -6
- package/dist/powered-file-system.d.ts +117 -47
- package/dist/powered-file-system.js +17 -39
- package/dist/powered-file-system.test.js +25 -0
- package/dist/recurse-io-sync.d.ts +7 -2
- package/dist/recurse-io-sync.js +38 -32
- package/dist/recurse-io.d.ts +11 -2
- package/dist/recurse-io.js +122 -57
- package/dist/suite.test.js +1 -1
- package/package.json +11 -16
- package/readme.md +133 -12
|
@@ -25,9 +25,12 @@ const append_1 = require("./powered-file-system/append");
|
|
|
25
25
|
const chmod_1 = require("./powered-file-system/chmod");
|
|
26
26
|
const chown_1 = require("./powered-file-system/chown");
|
|
27
27
|
const copy_1 = require("./powered-file-system/copy");
|
|
28
|
+
const empty_dir_1 = require("./powered-file-system/empty-dir");
|
|
28
29
|
const mkdir_1 = require("./powered-file-system/mkdir");
|
|
29
30
|
const read_1 = require("./powered-file-system/read");
|
|
31
|
+
const readlink_1 = require("./powered-file-system/readlink");
|
|
30
32
|
const readdir_1 = require("./powered-file-system/readdir");
|
|
33
|
+
const realpath_1 = require("./powered-file-system/realpath");
|
|
31
34
|
const remove_1 = require("./powered-file-system/remove");
|
|
32
35
|
const rename_1 = require("./powered-file-system/rename");
|
|
33
36
|
const stat_1 = require("./powered-file-system/stat");
|
|
@@ -38,8 +41,7 @@ __exportStar(require("./bitmask"), exports);
|
|
|
38
41
|
/**
|
|
39
42
|
* Path-aware wrapper around Node's file system APIs.
|
|
40
43
|
*
|
|
41
|
-
*
|
|
42
|
-
* suitable for sandboxed or virtual working-directory workflows.
|
|
44
|
+
* Relative paths are resolved against `pwd`; absolute paths are preserved.
|
|
43
45
|
*/
|
|
44
46
|
class PoweredFileSystem {
|
|
45
47
|
pwd;
|
|
@@ -63,80 +65,56 @@ class PoweredFileSystem {
|
|
|
63
65
|
this.pwd = pwd ? node_path_1.default.resolve(pwd) : process.cwd();
|
|
64
66
|
}
|
|
65
67
|
/**
|
|
66
|
-
*
|
|
68
|
+
* Resolves relative paths against `pwd` while preserving absolute paths.
|
|
67
69
|
*/
|
|
70
|
+
resolve(src) {
|
|
71
|
+
return node_path_1.default.resolve(this.pwd, src);
|
|
72
|
+
}
|
|
68
73
|
test(src, options) {
|
|
69
74
|
return test_1.test.call(this, src, options);
|
|
70
75
|
}
|
|
71
|
-
/**
|
|
72
|
-
* Returns `lstat` information for a path.
|
|
73
|
-
*/
|
|
74
76
|
stat(src, options) {
|
|
75
77
|
return stat_1.stat.call(this, src, options);
|
|
76
78
|
}
|
|
77
|
-
/**
|
|
78
|
-
* Applies a mode recursively to a file or directory tree.
|
|
79
|
-
*/
|
|
80
79
|
chmod(src, mode, options) {
|
|
81
80
|
return chmod_1.chmod.call(this, src, mode, options);
|
|
82
81
|
}
|
|
83
|
-
/**
|
|
84
|
-
* Applies ownership recursively to a file or directory tree.
|
|
85
|
-
*/
|
|
86
82
|
chown(src, options) {
|
|
87
83
|
return chown_1.chown.call(this, src, options);
|
|
88
84
|
}
|
|
89
|
-
/**
|
|
90
|
-
* Creates a symbolic link from `dest` to `src`.
|
|
91
|
-
*/
|
|
92
85
|
symlink(src, dest, options) {
|
|
93
86
|
return symlink_1.symlink.call(this, src, dest, options);
|
|
94
87
|
}
|
|
95
|
-
/**
|
|
96
|
-
* Copies `src` into the destination directory.
|
|
97
|
-
*/
|
|
98
88
|
copy(src, dest, options) {
|
|
99
89
|
return copy_1.copy.call(this, src, dest, options);
|
|
100
90
|
}
|
|
101
|
-
/**
|
|
102
|
-
* Renames or moves a file system node.
|
|
103
|
-
*/
|
|
104
91
|
rename(src, dest, options) {
|
|
105
92
|
return rename_1.rename.call(this, src, dest, options);
|
|
106
93
|
}
|
|
107
|
-
/**
|
|
108
|
-
* Removes a file system node recursively.
|
|
109
|
-
*/
|
|
110
94
|
remove(src, options) {
|
|
111
95
|
return remove_1.remove.call(this, src, options);
|
|
112
96
|
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
97
|
+
emptyDir(src, options) {
|
|
98
|
+
return empty_dir_1.emptyDir.call(this, src, options);
|
|
99
|
+
}
|
|
116
100
|
read(src, options) {
|
|
117
101
|
return read_1.read.call(this, src, options);
|
|
118
102
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Writes a file and applies the resulting permissions explicitly.
|
|
121
|
-
*/
|
|
122
103
|
write(src, data, options) {
|
|
123
104
|
return write_1.write.call(this, src, data, options);
|
|
124
105
|
}
|
|
125
|
-
/**
|
|
126
|
-
* @deprecated Use `write(..., { flag: 'a' })` instead.
|
|
127
|
-
*/
|
|
128
106
|
append(src, data, options) {
|
|
129
107
|
return append_1.append.call(this, src, data, options);
|
|
130
108
|
}
|
|
131
|
-
/**
|
|
132
|
-
* Lists directory entries relative to the current instance root.
|
|
133
|
-
*/
|
|
134
109
|
readdir(dir, options) {
|
|
135
110
|
return readdir_1.readdir.call(this, dir, options);
|
|
136
111
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
112
|
+
readlink(src, options) {
|
|
113
|
+
return readlink_1.readlink.call(this, src, options);
|
|
114
|
+
}
|
|
115
|
+
realpath(src, options) {
|
|
116
|
+
return realpath_1.realpath.call(this, src, options);
|
|
117
|
+
}
|
|
140
118
|
mkdir(dir, options) {
|
|
141
119
|
return mkdir_1.mkdir.call(this, dir, options);
|
|
142
120
|
}
|
|
@@ -4,8 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const node_assert_1 = __importDefault(require("node:assert"));
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
7
8
|
const node_test_1 = require("node:test");
|
|
8
9
|
const index_1 = require("./index");
|
|
10
|
+
const test_utils_1 = require("./test-utils");
|
|
9
11
|
/**
|
|
10
12
|
* Verifies constructor path resolution semantics for the main API surface.
|
|
11
13
|
*/
|
|
@@ -18,4 +20,27 @@ const index_1 = require("./index");
|
|
|
18
20
|
const { pwd } = new index_1.PoweredFileSystem(__dirname);
|
|
19
21
|
(0, node_assert_1.default)(pwd === __dirname);
|
|
20
22
|
});
|
|
23
|
+
(0, node_test_1.it)('Positive: Resolve should accept paths inside the working directory', () => {
|
|
24
|
+
const tmpDir = (0, test_utils_1.createTmpDir)();
|
|
25
|
+
try {
|
|
26
|
+
const pfs = new index_1.PoweredFileSystem(tmpDir);
|
|
27
|
+
const resolved = pfs.resolve('./nested/file.txt');
|
|
28
|
+
(0, node_assert_1.default)(resolved === node_path_1.default.join(tmpDir, 'nested', 'file.txt'));
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
(0, test_utils_1.restore)(tmpDir);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
(0, node_test_1.it)('Positive: Resolve should preserve absolute paths outside the working directory', () => {
|
|
35
|
+
const tmpDir = (0, test_utils_1.createTmpDir)();
|
|
36
|
+
const outsidePath = node_path_1.default.resolve(node_path_1.default.dirname(tmpDir), 'outside.txt');
|
|
37
|
+
try {
|
|
38
|
+
const pfs = new index_1.PoweredFileSystem(tmpDir);
|
|
39
|
+
const resolved = pfs.resolve(outsidePath);
|
|
40
|
+
(0, node_assert_1.default)(resolved === outsidePath);
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
(0, test_utils_1.restore)(tmpDir);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
21
46
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ICopyOptions } from './recurse-io';
|
|
1
2
|
/**
|
|
2
3
|
* Synchronous counterpart of the recursive chmod implementation.
|
|
3
4
|
*/
|
|
@@ -5,15 +6,19 @@ export declare function chmodSync(src: string, mode: number): void;
|
|
|
5
6
|
/**
|
|
6
7
|
* Synchronous counterpart of the recursive chown implementation.
|
|
7
8
|
*/
|
|
8
|
-
export declare function chownSync(src: string, uid: number, gid: number): void;
|
|
9
|
+
export declare function chownSync(src: string, uid: number | undefined, gid: number | undefined): void;
|
|
9
10
|
/**
|
|
10
11
|
* Synchronously copies a file system node into the target directory.
|
|
11
12
|
*/
|
|
12
|
-
export declare function copySync(src: string, dir: string,
|
|
13
|
+
export declare function copySync(src: string, dir: string, options: ICopyOptions): void;
|
|
13
14
|
/**
|
|
14
15
|
* Synchronously removes files, directories, and symlinks without following links.
|
|
15
16
|
*/
|
|
16
17
|
export declare function removeSync(src: string): void;
|
|
18
|
+
/**
|
|
19
|
+
* Synchronously removes all entries inside a directory while preserving it.
|
|
20
|
+
*/
|
|
21
|
+
export declare function emptyDirSync(src: string): void;
|
|
17
22
|
/**
|
|
18
23
|
* Synchronously creates a directory tree using permissions derived from umask.
|
|
19
24
|
*/
|
package/dist/recurse-io-sync.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.chmodSync = chmodSync;
|
|
|
7
7
|
exports.chownSync = chownSync;
|
|
8
8
|
exports.copySync = copySync;
|
|
9
9
|
exports.removeSync = removeSync;
|
|
10
|
+
exports.emptyDirSync = emptyDirSync;
|
|
10
11
|
exports.mkdirSync = mkdirSync;
|
|
11
12
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
13
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -28,60 +29,65 @@ function chmodSync(src, mode) {
|
|
|
28
29
|
*/
|
|
29
30
|
function chownSync(src, uid, gid) {
|
|
30
31
|
const stats = node_fs_1.default.statSync(src);
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (gid === 0) {
|
|
35
|
-
gid = stats.gid;
|
|
36
|
-
}
|
|
32
|
+
// `0` is a valid uid/gid, so only nullish values mean "preserve current owner".
|
|
33
|
+
const nextUid = uid ?? stats.uid;
|
|
34
|
+
const nextGid = gid ?? stats.gid;
|
|
37
35
|
if (stats.isDirectory()) {
|
|
38
36
|
const list = node_fs_1.default.readdirSync(src);
|
|
39
37
|
for (const loc of list) {
|
|
40
|
-
chownSync(node_path_1.default.join(src, loc),
|
|
38
|
+
chownSync(node_path_1.default.join(src, loc), nextUid, nextGid);
|
|
41
39
|
}
|
|
42
40
|
}
|
|
43
|
-
node_fs_1.default.chownSync(src,
|
|
41
|
+
node_fs_1.default.chownSync(src, nextUid, nextGid);
|
|
44
42
|
}
|
|
45
43
|
/**
|
|
46
44
|
* Synchronously copies a file system node into the target directory.
|
|
47
45
|
*/
|
|
48
|
-
function copySync(src, dir,
|
|
46
|
+
function copySync(src, dir, options) {
|
|
49
47
|
const stat = node_fs_1.default.statSync(src);
|
|
48
|
+
const loc = node_path_1.default.basename(src);
|
|
49
|
+
const dest = node_path_1.default.join(dir, loc);
|
|
50
|
+
if (dest === src) {
|
|
51
|
+
throw new Error(`Source and destination are identical: ${src}`);
|
|
52
|
+
}
|
|
53
|
+
if (options.filter && options.filter(src, dest) === false) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
50
56
|
if (stat.isDirectory()) {
|
|
51
57
|
const list = node_fs_1.default.readdirSync(src);
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
const mode = 0o777 & ~options.umask;
|
|
59
|
+
// Overwrite is implemented as replace-before-copy to support directory targets.
|
|
60
|
+
if (options.overwrite && node_fs_1.default.existsSync(dest)) {
|
|
61
|
+
removeSync(dest);
|
|
62
|
+
}
|
|
63
|
+
node_fs_1.default.mkdirSync(dest, { mode });
|
|
56
64
|
for (const loc of list) {
|
|
57
|
-
copySync(node_path_1.default.join(src, loc),
|
|
65
|
+
copySync(node_path_1.default.join(src, loc), dest, options);
|
|
58
66
|
}
|
|
59
67
|
}
|
|
60
68
|
else {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
69
|
+
// Match directory behavior by replacing the existing target before writing.
|
|
70
|
+
if (options.overwrite && node_fs_1.default.existsSync(dest)) {
|
|
71
|
+
removeSync(dest);
|
|
72
|
+
}
|
|
73
|
+
const flags = options.overwrite ? 0 : node_fs_1.default.constants.COPYFILE_EXCL;
|
|
74
|
+
node_fs_1.default.copyFileSync(src, dest, flags);
|
|
75
|
+
node_fs_1.default.chmodSync(dest, 0o666 & ~options.umask);
|
|
65
76
|
}
|
|
66
77
|
}
|
|
67
78
|
/**
|
|
68
79
|
* Synchronously removes files, directories, and symlinks without following links.
|
|
69
80
|
*/
|
|
70
81
|
function removeSync(src) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
node_fs_1.default.rmdirSync(src);
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
node_fs_1.default.unlinkSync(src);
|
|
82
|
+
node_fs_1.default.rmSync(src, { recursive: true, force: false });
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Synchronously removes all entries inside a directory while preserving it.
|
|
86
|
+
*/
|
|
87
|
+
function emptyDirSync(src) {
|
|
88
|
+
const list = node_fs_1.default.readdirSync(src);
|
|
89
|
+
for (const loc of list) {
|
|
90
|
+
node_fs_1.default.rmSync(node_path_1.default.join(src, loc), { recursive: true, force: false });
|
|
85
91
|
}
|
|
86
92
|
}
|
|
87
93
|
/**
|
package/dist/recurse-io.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { NoParamCallback } from 'node:fs';
|
|
2
|
+
export interface ICopyOptions {
|
|
3
|
+
umask: number;
|
|
4
|
+
overwrite: boolean;
|
|
5
|
+
filter?: (src: string, dest: string) => boolean;
|
|
6
|
+
}
|
|
2
7
|
/**
|
|
3
8
|
* Applies chmod depth-first so directories are updated after their contents.
|
|
4
9
|
*/
|
|
@@ -6,15 +11,19 @@ export declare function chmod(src: string, mode: number, callback: NoParamCallba
|
|
|
6
11
|
/**
|
|
7
12
|
* Applies ownership recursively while preserving current values when uid/gid are omitted.
|
|
8
13
|
*/
|
|
9
|
-
export declare function chown(src: string, uid: number, gid: number, callback: NoParamCallback): void;
|
|
14
|
+
export declare function chown(src: string, uid: number | undefined, gid: number | undefined, callback: NoParamCallback): void;
|
|
10
15
|
/**
|
|
11
16
|
* Copies a file system node into the target directory, creating directories as needed.
|
|
12
17
|
*/
|
|
13
|
-
export declare function copy(src: string, dir: string,
|
|
18
|
+
export declare function copy(src: string, dir: string, options: ICopyOptions, callback: NoParamCallback): void;
|
|
14
19
|
/**
|
|
15
20
|
* Removes files, directories, and symlinks without following symbolic links.
|
|
16
21
|
*/
|
|
17
22
|
export declare function remove(src: string, callback: NoParamCallback): void;
|
|
23
|
+
/**
|
|
24
|
+
* Removes all entries inside a directory while preserving the directory itself.
|
|
25
|
+
*/
|
|
26
|
+
export declare function emptyDir(src: string, callback: NoParamCallback): void;
|
|
18
27
|
/**
|
|
19
28
|
* Creates a directory tree with the permissions derived from the provided umask.
|
|
20
29
|
*/
|
package/dist/recurse-io.js
CHANGED
|
@@ -7,13 +7,26 @@ exports.chmod = chmod;
|
|
|
7
7
|
exports.chown = chown;
|
|
8
8
|
exports.copy = copy;
|
|
9
9
|
exports.remove = remove;
|
|
10
|
+
exports.emptyDir = emptyDir;
|
|
10
11
|
exports.mkdir = mkdir;
|
|
11
12
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
12
13
|
const node_path_1 = __importDefault(require("node:path"));
|
|
14
|
+
function once(callback) {
|
|
15
|
+
let called = false;
|
|
16
|
+
return (err) => {
|
|
17
|
+
// Recursive branches can fail concurrently; report only the first terminal result.
|
|
18
|
+
if (called) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
called = true;
|
|
22
|
+
callback(err);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
13
25
|
/**
|
|
14
26
|
* Applies chmod depth-first so directories are updated after their contents.
|
|
15
27
|
*/
|
|
16
28
|
function chmod(src, mode, callback) {
|
|
29
|
+
callback = once(callback);
|
|
17
30
|
let reduce = 0;
|
|
18
31
|
node_fs_1.default.stat(src, (err, stats) => {
|
|
19
32
|
if (err)
|
|
@@ -48,95 +61,153 @@ function chmod(src, mode, callback) {
|
|
|
48
61
|
* Applies ownership recursively while preserving current values when uid/gid are omitted.
|
|
49
62
|
*/
|
|
50
63
|
function chown(src, uid, gid, callback) {
|
|
64
|
+
callback = once(callback);
|
|
51
65
|
let reduce = 0;
|
|
52
66
|
node_fs_1.default.stat(src, (err, stats) => {
|
|
53
67
|
if (err) {
|
|
54
68
|
return callback(err);
|
|
55
69
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (gid === 0) {
|
|
60
|
-
gid = stats.gid;
|
|
61
|
-
}
|
|
70
|
+
// `0` is a valid uid/gid, so only nullish values mean "preserve current owner".
|
|
71
|
+
const nextUid = uid ?? stats.uid;
|
|
72
|
+
const nextGid = gid ?? stats.gid;
|
|
62
73
|
if (stats.isDirectory()) {
|
|
63
74
|
node_fs_1.default.readdir(src, (err, list) => {
|
|
64
75
|
if (err) {
|
|
65
76
|
return callback(err);
|
|
66
77
|
}
|
|
67
78
|
if (list.length === 0) {
|
|
68
|
-
return node_fs_1.default.chown(src,
|
|
79
|
+
return node_fs_1.default.chown(src, nextUid, nextGid, callback);
|
|
69
80
|
}
|
|
70
81
|
reduce = list.length;
|
|
71
82
|
for (const loc of list) {
|
|
72
|
-
chown(node_path_1.default.join(src, loc),
|
|
83
|
+
chown(node_path_1.default.join(src, loc), nextUid, nextGid, (err) => {
|
|
73
84
|
if (err) {
|
|
74
85
|
return callback(err);
|
|
75
86
|
}
|
|
76
87
|
if (--reduce === 0) {
|
|
77
|
-
node_fs_1.default.chown(src,
|
|
88
|
+
node_fs_1.default.chown(src, nextUid, nextGid, callback);
|
|
78
89
|
}
|
|
79
90
|
});
|
|
80
91
|
}
|
|
81
92
|
});
|
|
82
93
|
}
|
|
83
94
|
else {
|
|
84
|
-
node_fs_1.default.chown(src,
|
|
95
|
+
node_fs_1.default.chown(src, nextUid, nextGid, callback);
|
|
85
96
|
}
|
|
86
97
|
});
|
|
87
98
|
}
|
|
88
99
|
/**
|
|
89
100
|
* Copies a file system node into the target directory, creating directories as needed.
|
|
90
101
|
*/
|
|
91
|
-
function copy(src, dir,
|
|
102
|
+
function copy(src, dir, options, callback) {
|
|
103
|
+
callback = once(callback);
|
|
92
104
|
node_fs_1.default.stat(src, (err, stat) => {
|
|
93
105
|
if (err) {
|
|
94
106
|
return callback(err);
|
|
95
107
|
}
|
|
108
|
+
const loc = node_path_1.default.basename(src);
|
|
109
|
+
const dest = node_path_1.default.join(dir, loc);
|
|
110
|
+
if (dest === src) {
|
|
111
|
+
return callback(new Error(`Source and destination are identical: ${src}`));
|
|
112
|
+
}
|
|
113
|
+
if (options.filter && options.filter(src, dest) === false) {
|
|
114
|
+
return callback(null);
|
|
115
|
+
}
|
|
96
116
|
if (stat.isDirectory()) {
|
|
97
117
|
node_fs_1.default.readdir(src, (err, list) => {
|
|
98
118
|
if (err) {
|
|
99
119
|
return callback(err);
|
|
100
120
|
}
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
121
|
+
const mode = 0o777 & ~options.umask;
|
|
122
|
+
const create = () => {
|
|
123
|
+
node_fs_1.default.mkdir(dest, { mode }, (err) => {
|
|
124
|
+
if (err) {
|
|
125
|
+
if (err.code === 'EEXIST') {
|
|
126
|
+
err = new Error(`Target already exists: ${dest}`);
|
|
127
|
+
}
|
|
128
|
+
return callback(err);
|
|
129
|
+
}
|
|
130
|
+
if (list.length === 0) {
|
|
131
|
+
return callback(null);
|
|
132
|
+
}
|
|
133
|
+
let reduce = list.length;
|
|
134
|
+
for (const item of list) {
|
|
135
|
+
copy(node_path_1.default.join(src, item), dest, options, (err) => {
|
|
136
|
+
if (err) {
|
|
137
|
+
return callback(err);
|
|
138
|
+
}
|
|
139
|
+
if (--reduce === 0) {
|
|
140
|
+
callback(null);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
};
|
|
146
|
+
if (!options.overwrite) {
|
|
147
|
+
return create();
|
|
148
|
+
}
|
|
149
|
+
// Overwrite is implemented as replace-before-copy to support directory targets.
|
|
150
|
+
node_fs_1.default.lstat(dest, (err, destStat) => {
|
|
105
151
|
if (err) {
|
|
106
|
-
if (err.code === '
|
|
107
|
-
|
|
152
|
+
if (err.code === 'ENOENT') {
|
|
153
|
+
return create();
|
|
108
154
|
}
|
|
109
155
|
return callback(err);
|
|
110
156
|
}
|
|
111
|
-
if (
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
let reduce = list.length;
|
|
115
|
-
for (const item of list) {
|
|
116
|
-
copy(node_path_1.default.join(src, item), destDir, umask, (err) => {
|
|
157
|
+
if (destStat.isDirectory()) {
|
|
158
|
+
return remove(dest, (err) => {
|
|
117
159
|
if (err) {
|
|
118
160
|
return callback(err);
|
|
119
161
|
}
|
|
120
|
-
|
|
121
|
-
callback(null);
|
|
122
|
-
}
|
|
162
|
+
create();
|
|
123
163
|
});
|
|
124
164
|
}
|
|
165
|
+
node_fs_1.default.unlink(dest, (err) => {
|
|
166
|
+
if (err) {
|
|
167
|
+
return callback(err);
|
|
168
|
+
}
|
|
169
|
+
create();
|
|
170
|
+
});
|
|
125
171
|
});
|
|
126
172
|
});
|
|
127
173
|
}
|
|
128
174
|
else {
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
175
|
+
const mode = 0o666 & ~options.umask;
|
|
176
|
+
const write = () => {
|
|
177
|
+
const flags = options.overwrite ? 0 : node_fs_1.default.constants.COPYFILE_EXCL;
|
|
178
|
+
node_fs_1.default.copyFile(src, dest, flags, (err) => {
|
|
179
|
+
if (err) {
|
|
180
|
+
return callback(err);
|
|
181
|
+
}
|
|
182
|
+
node_fs_1.default.chmod(dest, mode, callback);
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
if (!options.overwrite) {
|
|
186
|
+
return write();
|
|
187
|
+
}
|
|
188
|
+
// Match directory behavior by replacing the existing target before writing.
|
|
189
|
+
node_fs_1.default.lstat(dest, (err, destStat) => {
|
|
190
|
+
if (err) {
|
|
191
|
+
if (err.code === 'ENOENT') {
|
|
192
|
+
return write();
|
|
193
|
+
}
|
|
194
|
+
return callback(err);
|
|
195
|
+
}
|
|
196
|
+
if (destStat.isDirectory()) {
|
|
197
|
+
return remove(dest, (err) => {
|
|
198
|
+
if (err) {
|
|
199
|
+
return callback(err);
|
|
200
|
+
}
|
|
201
|
+
write();
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
node_fs_1.default.unlink(dest, (err) => {
|
|
205
|
+
if (err) {
|
|
206
|
+
return callback(err);
|
|
207
|
+
}
|
|
208
|
+
write();
|
|
209
|
+
});
|
|
138
210
|
});
|
|
139
|
-
readStream.pipe(writeStream);
|
|
140
211
|
}
|
|
141
212
|
});
|
|
142
213
|
}
|
|
@@ -144,37 +215,31 @@ function copy(src, dir, umask, callback) {
|
|
|
144
215
|
* Removes files, directories, and symlinks without following symbolic links.
|
|
145
216
|
*/
|
|
146
217
|
function remove(src, callback) {
|
|
147
|
-
node_fs_1.default.
|
|
218
|
+
node_fs_1.default.rm(src, { recursive: true, force: false }, once(callback));
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Removes all entries inside a directory while preserving the directory itself.
|
|
222
|
+
*/
|
|
223
|
+
function emptyDir(src, callback) {
|
|
224
|
+
callback = once(callback);
|
|
225
|
+
node_fs_1.default.readdir(src, (err, list) => {
|
|
148
226
|
if (err) {
|
|
149
227
|
return callback(err);
|
|
150
228
|
}
|
|
151
|
-
if (
|
|
152
|
-
return
|
|
229
|
+
if (list.length === 0) {
|
|
230
|
+
return callback(null);
|
|
153
231
|
}
|
|
154
|
-
|
|
155
|
-
|
|
232
|
+
let reduce = list.length;
|
|
233
|
+
for (const loc of list) {
|
|
234
|
+
node_fs_1.default.rm(node_path_1.default.join(src, loc), { recursive: true, force: false }, (err) => {
|
|
156
235
|
if (err) {
|
|
157
236
|
return callback(err);
|
|
158
237
|
}
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
let reduce = list.length;
|
|
163
|
-
for (const loc of list) {
|
|
164
|
-
remove(node_path_1.default.join(src, loc), (err) => {
|
|
165
|
-
if (err) {
|
|
166
|
-
return callback(err);
|
|
167
|
-
}
|
|
168
|
-
if (--reduce === 0) {
|
|
169
|
-
node_fs_1.default.rmdir(src, callback);
|
|
170
|
-
}
|
|
171
|
-
});
|
|
238
|
+
if (--reduce === 0) {
|
|
239
|
+
callback(null);
|
|
172
240
|
}
|
|
173
241
|
});
|
|
174
242
|
}
|
|
175
|
-
else {
|
|
176
|
-
node_fs_1.default.unlink(src, callback);
|
|
177
|
-
}
|
|
178
243
|
});
|
|
179
244
|
}
|
|
180
245
|
/**
|
package/dist/suite.test.js
CHANGED
|
@@ -31,7 +31,7 @@ const distDir = node_path_1.default.resolve(__dirname);
|
|
|
31
31
|
// The runner skips itself and forwards the rest to Node's native test harness.
|
|
32
32
|
const testFiles = collectTestFiles(distDir).filter((file) => file !== __filename);
|
|
33
33
|
if (!testFiles.length) {
|
|
34
|
-
console.warn("
|
|
34
|
+
console.warn("No test files found in dist/");
|
|
35
35
|
process.exit(0);
|
|
36
36
|
}
|
|
37
37
|
const { status } = (0, node_child_process_1.spawnSync)(process.execPath, ['--test', ...testFiles], {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pwd-fs",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Path-aware file system utilities with scoped working directories and recursive operations",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"umask",
|
|
@@ -31,16 +31,11 @@
|
|
|
31
31
|
"url": "https://github.com/woodger/pwd-fs/issues"
|
|
32
32
|
},
|
|
33
33
|
"engines": {
|
|
34
|
-
"node": ">=
|
|
35
|
-
},
|
|
36
|
-
"main": "./dist/index.js",
|
|
37
|
-
"types": "./dist/index.d.ts",
|
|
38
|
-
"exports": {
|
|
39
|
-
".": {
|
|
40
|
-
"types": "./dist/index.d.ts",
|
|
41
|
-
"default": "./dist/index.js"
|
|
42
|
-
}
|
|
34
|
+
"node": ">=18"
|
|
43
35
|
},
|
|
36
|
+
"packageManager": "yarn@1.22.22",
|
|
37
|
+
"main": "dist/index.js",
|
|
38
|
+
"types": "dist/index.d.ts",
|
|
44
39
|
"files": [
|
|
45
40
|
"dist",
|
|
46
41
|
"readme.md",
|
|
@@ -53,12 +48,12 @@
|
|
|
53
48
|
},
|
|
54
49
|
"devDependencies": {
|
|
55
50
|
"@eslint/js": "^10.0.1",
|
|
56
|
-
"@types/chance": "^1.1.
|
|
57
|
-
"@types/node": "^
|
|
51
|
+
"@types/chance": "^1.1.8",
|
|
52
|
+
"@types/node": "^25.6.2",
|
|
58
53
|
"chance": "^1.1.13",
|
|
59
|
-
"eslint": "^10.
|
|
60
|
-
"globals": "^17.
|
|
61
|
-
"typescript": "^
|
|
62
|
-
"typescript-eslint": "^8.
|
|
54
|
+
"eslint": "^10.3.0",
|
|
55
|
+
"globals": "^17.6.0",
|
|
56
|
+
"typescript": "^6.0.3",
|
|
57
|
+
"typescript-eslint": "^8.59.2"
|
|
63
58
|
}
|
|
64
59
|
}
|