@@ -4,7 +4,7 @@ import {FakeFS} from '../FakeFS';
4
4
import { Path , convertPath } from '../path' ;
5
5
6
6
// 1980-01-01, like Fedora
7
- const defaultTime = 315532800 ;
7
+ const defaultTime = new Date ( 315532800 * 1000 ) ;
8
8
9
9
export type CopyOptions = {
10
10
stableTime : boolean ,
@@ -45,32 +45,40 @@ async function copyImpl<P1 extends Path, P2 extends Path>(prelayout: Operations,
45
45
const destinationStat = await maybeLStat ( destinationFs , destination ) ;
46
46
const sourceStat = await sourceFs . lstatPromise ( source ) ;
47
47
48
- if ( opts . stableTime )
49
- postlayout . push ( ( ) => updateTime ( destination , defaultTime , defaultTime ) ) ;
50
- else
51
- postlayout . push ( ( ) => updateTime ( destination , sourceStat . atime , sourceStat . mtime ) ) ;
48
+ const referenceTime = opts . stableTime
49
+ ? { mtime : defaultTime , atime : defaultTime } as const
50
+ : sourceStat ;
52
51
52
+ let updated : boolean ;
53
53
switch ( true ) {
54
54
case sourceStat . isDirectory ( ) : {
55
- await copyFolder ( prelayout , postlayout , updateTime , destinationFs , destination , destinationStat , sourceFs , source , sourceStat , opts ) ;
55
+ updated = await copyFolder ( prelayout , postlayout , updateTime , destinationFs , destination , destinationStat , sourceFs , source , sourceStat , opts ) ;
56
56
} break ;
57
57
58
58
case sourceStat . isFile ( ) : {
59
- await copyFile ( prelayout , postlayout , updateTime , destinationFs , destination , destinationStat , sourceFs , source , sourceStat , opts ) ;
59
+ updated = await copyFile ( prelayout , postlayout , updateTime , destinationFs , destination , destinationStat , sourceFs , source , sourceStat , opts ) ;
60
60
} break ;
61
61
62
62
case sourceStat . isSymbolicLink ( ) : {
63
- await copySymlink ( prelayout , postlayout , updateTime , destinationFs , destination , destinationStat , sourceFs , source , sourceStat , opts ) ;
63
+ updated = await copySymlink ( prelayout , postlayout , updateTime , destinationFs , destination , destinationStat , sourceFs , source , sourceStat , opts ) ;
64
64
} break ;
65
65
66
66
default : {
67
67
throw new Error ( `Unsupported file type (${ sourceStat . mode } )` ) ;
68
68
} break ;
69
69
}
70
70
71
- postlayout . push ( ( ) => {
72
- return destinationFs . chmodPromise ( destination , sourceStat . mode & 0o777 ) ;
73
- } ) ;
71
+ if ( updated || destinationStat ?. mtime ?. getTime ( ) !== referenceTime . mtime . getTime ( ) || destinationStat ?. atime ?. getTime ( ) !== referenceTime . atime . getTime ( ) ) {
72
+ postlayout . push ( ( ) => updateTime ( destination , referenceTime . atime , referenceTime . mtime ) ) ;
73
+ updated = true ;
74
+ }
75
+
76
+ if ( destinationStat === null || ( destinationStat . mode & 0o777 ) !== ( sourceStat . mode & 0o777 ) ) {
77
+ postlayout . push ( ( ) => destinationFs . chmodPromise ( destination , sourceStat . mode & 0o777 ) ) ;
78
+ updated = true ;
79
+ }
80
+
81
+ return updated ;
74
82
}
75
83
76
84
async function maybeLStat < P extends Path > ( baseFs : FakeFS < P > , p : P ) {
@@ -87,24 +95,36 @@ async function copyFolder<P1 extends Path, P2 extends Path>(prelayout: Operation
87
95
prelayout . push ( async ( ) => destinationFs . removePromise ( destination ) ) ;
88
96
destinationStat = null ;
89
97
} else {
90
- return ;
98
+ return false ;
91
99
}
92
100
}
93
101
94
- if ( destinationStat === null )
102
+ let updated = false ;
103
+
104
+ if ( destinationStat === null ) {
95
105
prelayout . push ( async ( ) => destinationFs . mkdirPromise ( destination , { mode : sourceStat . mode } ) ) ;
106
+ updated = true ;
107
+ }
96
108
97
109
const entries = await sourceFs . readdirPromise ( source ) ;
98
110
99
111
if ( opts . stableSort ) {
100
112
for ( const entry of entries . sort ( ) ) {
101
- await copyImpl ( prelayout , postlayout , updateTime , destinationFs , destinationFs . pathUtils . join ( destination , entry ) , sourceFs , sourceFs . pathUtils . join ( source , entry ) , opts ) ;
113
+ if ( await copyImpl ( prelayout , postlayout , updateTime , destinationFs , destinationFs . pathUtils . join ( destination , entry ) , sourceFs , sourceFs . pathUtils . join ( source , entry ) , opts ) ) {
114
+ updated = true ;
115
+ }
102
116
}
103
117
} else {
104
- await Promise . all ( entries . map ( async entry => {
118
+ const entriesUpdateStatus = await Promise . all ( entries . map ( async entry => {
105
119
await copyImpl ( prelayout , postlayout , updateTime , destinationFs , destinationFs . pathUtils . join ( destination , entry ) , sourceFs , sourceFs . pathUtils . join ( source , entry ) , opts ) ;
106
120
} ) ) ;
121
+
122
+ if ( entriesUpdateStatus . some ( status => status ) ) {
123
+ updated = true ;
124
+ }
107
125
}
126
+
127
+ return updated ;
108
128
}
109
129
110
130
async function copyFile < P1 extends Path , P2 extends Path > ( prelayout : Operations , postlayout : Operations , updateTime : typeof FakeFS . prototype . utimesPromise , destinationFs : FakeFS < P1 > , destination : P1 , destinationStat : Stats | null , sourceFs : FakeFS < P2 > , source : P2 , sourceStat : Stats , opts : CopyOptions ) {
@@ -113,15 +133,16 @@ async function copyFile<P1 extends Path, P2 extends Path>(prelayout: Operations,
113
133
prelayout . push ( async ( ) => destinationFs . removePromise ( destination ) ) ;
114
134
destinationStat = null ;
115
135
} else {
116
- return ;
136
+ return false ;
117
137
}
118
138
}
119
139
120
- if ( destinationFs as any === sourceFs as any ) {
121
- prelayout . push ( async ( ) => destinationFs . copyFilePromise ( source as any as P1 , destination , fs . constants . COPYFILE_FICLONE ) ) ;
122
- } else {
123
- prelayout . push ( async ( ) => destinationFs . writeFilePromise ( destination , await sourceFs . readFilePromise ( source ) ) ) ;
124
- }
140
+ const op = destinationFs as any === sourceFs as any
141
+ ? async ( ) => destinationFs . copyFilePromise ( source as any as P1 , destination , fs . constants . COPYFILE_FICLONE )
142
+ : async ( ) => destinationFs . writeFilePromise ( destination , await sourceFs . readFilePromise ( source ) ) ;
143
+
144
+ prelayout . push ( async ( ) => op ( ) ) ;
145
+ return true ;
125
146
}
126
147
127
148
async function copySymlink < P1 extends Path , P2 extends Path > ( prelayout : Operations , postlayout : Operations , updateTime : typeof FakeFS . prototype . utimesPromise , destinationFs : FakeFS < P1 > , destination : P1 , destinationStat : Stats | null , sourceFs : FakeFS < P2 > , source : P2 , sourceStat : Stats , opts : CopyOptions ) {
@@ -130,10 +151,13 @@ async function copySymlink<P1 extends Path, P2 extends Path>(prelayout: Operatio
130
151
prelayout . push ( async ( ) => destinationFs . removePromise ( destination ) ) ;
131
152
destinationStat = null ;
132
153
} else {
133
- return ;
154
+ return false ;
134
155
}
135
156
}
136
157
137
- const target = await sourceFs . readlinkPromise ( source ) ;
138
- prelayout . push ( async ( ) => destinationFs . symlinkPromise ( convertPath ( destinationFs . pathUtils , target ) , destination ) ) ;
158
+ prelayout . push ( async ( ) => {
159
+ await destinationFs . symlinkPromise ( convertPath ( destinationFs . pathUtils , await sourceFs . readlinkPromise ( source ) ) , destination ) ;
160
+ } ) ;
161
+
162
+ return true ;
139
163
}
0 commit comments