@@ -4,36 +4,40 @@ import { copyFile, readdir, stat, constants } from 'node:fs/promises';
44import { join } from 'node:path' ;
55
66/**
7- * Safely copies files from source to target directory, skipping files that haven't changed
8- * based on file stats (size and modification time). Uses native fs.copyFile which handles
9- * concurrent operations gracefully.
7+ * Attempts to copy a file forcibly (`COPYFILE_FICLONE_FORCE`. Otherwise, falls back to a time-based check approach)
108 *
119 * @param {string } srcDir - Source directory path
1210 * @param {string } targetDir - Target directory path
1311 */
1412export async function safeCopy ( srcDir , targetDir ) {
15- const files = await readdir ( srcDir ) ;
13+ try {
14+ await copyFile ( srcDir , targetDir , constants . COPYFILE_FICLONE ) ;
15+ } catch ( err ) {
16+ if ( err ?. syscall !== 'copyfile' ) {
17+ throw err ;
18+ }
1619
17- for ( const file of files ) {
18- const sourcePath = join ( srcDir , file ) ;
19- const targetPath = join ( targetDir , file ) ;
20+ const files = await readdir ( srcDir ) ;
2021
21- const [ sStat , tStat ] = await Promise . allSettled ( [
22- stat ( sourcePath ) ,
23- stat ( targetPath ) ,
24- ] ) ;
22+ for ( const file of files ) {
23+ const sourcePath = join ( srcDir , file ) ;
24+ const targetPath = join ( targetDir , file ) ;
2525
26- const shouldWrite =
27- tStat . status === 'rejected' ||
28- sStat . value . size !== tStat . value . size ||
29- sStat . value . mtimeMs > tStat . value . mtimeMs ;
26+ const [ sStat , tStat ] = await Promise . allSettled ( [
27+ stat ( sourcePath ) ,
28+ stat ( targetPath ) ,
29+ ] ) ;
3030
31- if ( ! shouldWrite ) {
32- continue ;
33- }
31+ const shouldWrite =
32+ tStat . status === 'rejected' ||
33+ sStat . value . size !== tStat . value . size ||
34+ sStat . value . mtimeMs > tStat . value . mtimeMs ;
3435
35- // Use copyFile with COPYFILE_FICLONE flag for efficient copying
36- // This is atomic and handles concurrent operations better than manual read/write
37- await copyFile ( sourcePath , targetPath , constants . COPYFILE_FICLONE ) ;
36+ if ( ! shouldWrite ) {
37+ continue ;
38+ }
39+
40+ await copyFile ( sourcePath , targetPath ) ;
41+ }
3842 }
3943}
0 commit comments