-
-
Notifications
You must be signed in to change notification settings - Fork 22
/
index.js
134 lines (111 loc) · 3.83 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import path from 'node:path';
import realFS, {constants as fsConstants} from 'node:fs';
import realFSPromises from 'node:fs/promises';
import {pEvent} from 'p-event';
import CopyFileError from './copy-file-error.js';
import * as fs from './fs.js';
const resolvePath = (cwd, sourcePath, destinationPath) => ({
sourcePath: path.resolve(cwd, sourcePath),
destinationPath: path.resolve(cwd, destinationPath),
});
const checkSourceIsFile = (stat, source) => {
if (!stat.isFile()) {
throw Object.assign(new CopyFileError(`EISDIR: illegal operation on a directory '${source}'`), {
errno: -21,
code: 'EISDIR',
source,
});
}
};
export async function copyFile(sourcePath, destinationPath, options = {}) {
if (!sourcePath || !destinationPath) {
throw new CopyFileError('`source` and `destination` required');
}
if (options.cwd) {
({sourcePath, destinationPath} = resolvePath(options.cwd, sourcePath, destinationPath));
}
options = {
overwrite: true,
...options,
};
const stats = await fs.lstat(sourcePath);
const {size} = stats;
checkSourceIsFile(stats, sourcePath);
await fs.makeDirectory(path.dirname(destinationPath), {mode: options.directoryMode});
if (typeof options.onProgress === 'function') {
const readStream = await fs.createReadStream(sourcePath);
const writeStream = fs.createWriteStream(destinationPath, {flags: options.overwrite ? 'w' : 'wx'});
const emitProgress = writtenBytes => {
options.onProgress({
sourcePath: path.resolve(sourcePath),
destinationPath: path.resolve(destinationPath),
size,
writtenBytes,
percent: writtenBytes === size ? 1 : writtenBytes / size,
});
};
readStream.on('data', () => {
emitProgress(writeStream.bytesWritten);
});
let readError;
readStream.once('error', error => {
readError = new CopyFileError(`Cannot read from \`${sourcePath}\`: ${error.message}`, {cause: error});
});
let shouldUpdateStats = false;
try {
const writePromise = pEvent(writeStream, 'close');
readStream.pipe(writeStream);
await writePromise;
emitProgress(size);
shouldUpdateStats = true;
} catch (error) {
throw new CopyFileError(`Cannot write to \`${destinationPath}\`: ${error.message}`, {cause: error});
}
if (readError) {
throw readError;
}
if (shouldUpdateStats) {
const stats = await fs.lstat(sourcePath);
return Promise.all([
fs.utimes(destinationPath, stats.atime, stats.mtime),
fs.chmod(destinationPath, stats.mode),
]);
}
} else {
// eslint-disable-next-line no-bitwise
const flags = options.overwrite ? fsConstants.COPYFILE_FICLONE : (fsConstants.COPYFILE_FICLONE | fsConstants.COPYFILE_EXCL);
try {
await realFSPromises.copyFile(sourcePath, destinationPath, flags);
await Promise.all([
realFSPromises.utimes(destinationPath, stats.atime, stats.mtime),
realFSPromises.chmod(destinationPath, stats.mode),
]);
} catch (error) {
throw new CopyFileError(error.message, {cause: error});
}
}
}
export function copyFileSync(sourcePath, destinationPath, options = {}) {
if (!sourcePath || !destinationPath) {
throw new CopyFileError('`source` and `destination` required');
}
if (options.cwd) {
({sourcePath, destinationPath} = resolvePath(options.cwd, sourcePath, destinationPath));
}
options = {
overwrite: true,
...options,
};
const stats = fs.lstatSync(sourcePath);
checkSourceIsFile(stats, sourcePath);
fs.makeDirectorySync(path.dirname(destinationPath), {mode: options.directoryMode});
// eslint-disable-next-line no-bitwise
const flags = options.overwrite ? fsConstants.COPYFILE_FICLONE : (fsConstants.COPYFILE_FICLONE | fsConstants.COPYFILE_EXCL);
try {
realFS.copyFileSync(sourcePath, destinationPath, flags);
} catch (error) {
throw new CopyFileError(error.message, {cause: error});
}
fs.utimesSync(destinationPath, stats.atime, stats.mtime);
fs.chmod(destinationPath, stats.mode);
}