a95100470bafbce519c5fda4c5ffc18c72459768
[yaffs-website] / node_modules / fs-extra / lib / copy / ncp.js
1 // imported from ncp (this is temporary, will rewrite)
2
3 var fs = require('graceful-fs')
4 var path = require('path')
5 var utimes = require('../util/utimes')
6
7 function ncp (source, dest, options, callback) {
8   if (!callback) {
9     callback = options
10     options = {}
11   }
12
13   var basePath = process.cwd()
14   var currentPath = path.resolve(basePath, source)
15   var targetPath = path.resolve(basePath, dest)
16
17   var filter = options.filter
18   var transform = options.transform
19   var clobber = options.clobber !== false // default true
20   var dereference = options.dereference
21   var preserveTimestamps = options.preserveTimestamps === true
22
23   var started = 0
24   var finished = 0
25   var running = 0
26
27   var errored = false
28
29   startCopy(currentPath)
30
31   function startCopy (source) {
32     started++
33     if (filter) {
34       if (filter instanceof RegExp) {
35         console.warn('Warning: fs-extra: Passing a RegExp filter is deprecated, use a function')
36         if (!filter.test(source)) {
37           return doneOne(true)
38         }
39       } else if (typeof filter === 'function') {
40         if (!filter(source)) {
41           return doneOne(true)
42         }
43       }
44     }
45     return getStats(source)
46   }
47
48   function getStats (source) {
49     var stat = dereference ? fs.stat : fs.lstat
50     running++
51     stat(source, function (err, stats) {
52       if (err) return onError(err)
53
54       // We need to get the mode from the stats object and preserve it.
55       var item = {
56         name: source,
57         mode: stats.mode,
58         mtime: stats.mtime, // modified time
59         atime: stats.atime, // access time
60         stats: stats // temporary
61       }
62
63       if (stats.isDirectory()) {
64         return onDir(item)
65       } else if (stats.isFile() || stats.isCharacterDevice() || stats.isBlockDevice()) {
66         return onFile(item)
67       } else if (stats.isSymbolicLink()) {
68         // Symlinks don't really need to know about the mode.
69         return onLink(source)
70       }
71     })
72   }
73
74   function onFile (file) {
75     var target = file.name.replace(currentPath, targetPath.replace('$', '$$$$')) // escapes '$' with '$$'
76     isWritable(target, function (writable) {
77       if (writable) {
78         copyFile(file, target)
79       } else {
80         if (clobber) {
81           rmFile(target, function () {
82             copyFile(file, target)
83           })
84         } else {
85           var err = new Error('EEXIST: ' + target + ' already exists.')
86           err.code = 'EEXIST'
87           err.errno = -17
88           err.path = target
89           onError(err)
90         }
91       }
92     })
93   }
94
95   function copyFile (file, target) {
96     var readStream = fs.createReadStream(file.name)
97     var writeStream = fs.createWriteStream(target, { mode: file.mode })
98
99     readStream.on('error', onError)
100     writeStream.on('error', onError)
101
102     if (transform) {
103       transform(readStream, writeStream, file)
104     } else {
105       writeStream.on('open', function () {
106         readStream.pipe(writeStream)
107       })
108     }
109
110     writeStream.once('finish', function () {
111       fs.chmod(target, file.mode, function (err) {
112         if (err) return onError(err)
113         if (preserveTimestamps) {
114           utimes.utimesMillis(target, file.atime, file.mtime, function (err) {
115             if (err) return onError(err)
116             return doneOne()
117           })
118         } else {
119           doneOne()
120         }
121       })
122     })
123   }
124
125   function rmFile (file, done) {
126     fs.unlink(file, function (err) {
127       if (err) return onError(err)
128       return done()
129     })
130   }
131
132   function onDir (dir) {
133     var target = dir.name.replace(currentPath, targetPath.replace('$', '$$$$')) // escapes '$' with '$$'
134     isWritable(target, function (writable) {
135       if (writable) {
136         return mkDir(dir, target)
137       }
138       copyDir(dir.name)
139     })
140   }
141
142   function mkDir (dir, target) {
143     fs.mkdir(target, dir.mode, function (err) {
144       if (err) return onError(err)
145       // despite setting mode in fs.mkdir, doesn't seem to work
146       // so we set it here.
147       fs.chmod(target, dir.mode, function (err) {
148         if (err) return onError(err)
149         copyDir(dir.name)
150       })
151     })
152   }
153
154   function copyDir (dir) {
155     fs.readdir(dir, function (err, items) {
156       if (err) return onError(err)
157       items.forEach(function (item) {
158         startCopy(path.join(dir, item))
159       })
160       return doneOne()
161     })
162   }
163
164   function onLink (link) {
165     var target = link.replace(currentPath, targetPath)
166     fs.readlink(link, function (err, resolvedPath) {
167       if (err) return onError(err)
168       checkLink(resolvedPath, target)
169     })
170   }
171
172   function checkLink (resolvedPath, target) {
173     if (dereference) {
174       resolvedPath = path.resolve(basePath, resolvedPath)
175     }
176     isWritable(target, function (writable) {
177       if (writable) {
178         return makeLink(resolvedPath, target)
179       }
180       fs.readlink(target, function (err, targetDest) {
181         if (err) return onError(err)
182
183         if (dereference) {
184           targetDest = path.resolve(basePath, targetDest)
185         }
186         if (targetDest === resolvedPath) {
187           return doneOne()
188         }
189         return rmFile(target, function () {
190           makeLink(resolvedPath, target)
191         })
192       })
193     })
194   }
195
196   function makeLink (linkPath, target) {
197     fs.symlink(linkPath, target, function (err) {
198       if (err) return onError(err)
199       return doneOne()
200     })
201   }
202
203   function isWritable (path, done) {
204     fs.lstat(path, function (err) {
205       if (err) {
206         if (err.code === 'ENOENT') return done(true)
207         return done(false)
208       }
209       return done(false)
210     })
211   }
212
213   function onError (err) {
214     // ensure callback is defined & called only once:
215     if (!errored && callback !== undefined) {
216       errored = true
217       return callback(err)
218     }
219   }
220
221   function doneOne (skipped) {
222     if (!skipped) running--
223     finished++
224     if ((started === finished) && (running === 0)) {
225       if (callback !== undefined) {
226         return callback(null)
227       }
228     }
229   }
230 }
231
232 module.exports = ncp