1 | var fs = require('fs'),
|
2 | path = require('path');
|
3 |
|
4 | module.exports = ncp
|
5 | ncp.ncp = ncp
|
6 |
|
7 | function ncp (source, dest, options, callback) {
|
8 | if (!callback) {
|
9 | callback = options;
|
10 | options = {};
|
11 | }
|
12 |
|
13 | var basePath = process.cwd(),
|
14 | currentPath = path.resolve(basePath, source),
|
15 | targetPath = path.resolve(basePath, dest),
|
16 | filter = options.filter,
|
17 | transform = options.transform,
|
18 | clobber = options.clobber !== false,
|
19 | errs = null,
|
20 | started = 0,
|
21 | finished = 0,
|
22 | running = 0,
|
23 | limit = options.limit || ncp.limit || 16;
|
24 |
|
25 | limit = (limit < 1) ? 1 : (limit > 512) ? 512 : limit;
|
26 |
|
27 | startCopy(currentPath);
|
28 |
|
29 | function startCopy(source) {
|
30 | started++;
|
31 | if (filter) {
|
32 | if (filter instanceof RegExp) {
|
33 | if (!filter.test(source)) {
|
34 | return cb(true);
|
35 | }
|
36 | }
|
37 | else if (typeof filter === 'function') {
|
38 | if (!filter(source)) {
|
39 | return cb(true);
|
40 | }
|
41 | }
|
42 | }
|
43 | return getStats(source);
|
44 | }
|
45 |
|
46 | function getStats(source) {
|
47 | if (running >= limit) {
|
48 | return process.nextTick(function () {
|
49 | getStats(source);
|
50 | });
|
51 | }
|
52 | running++;
|
53 | fs.lstat(source, function (err, stats) {
|
54 | var item = {};
|
55 | if (err) {
|
56 | return onError(err);
|
57 | }
|
58 |
|
59 |
|
60 | item.name = source;
|
61 | item.mode = stats.mode;
|
62 |
|
63 | if (stats.isDirectory()) {
|
64 | return onDir(item);
|
65 | }
|
66 | else if (stats.isFile()) {
|
67 | return onFile(item);
|
68 | }
|
69 | else if (stats.isSymbolicLink()) {
|
70 |
|
71 | return onLink(source);
|
72 | }
|
73 | });
|
74 | }
|
75 |
|
76 | function onFile(file) {
|
77 | var target = file.name.replace(currentPath, targetPath);
|
78 | isWritable(target, function (writable) {
|
79 | if (writable) {
|
80 | return copyFile(file, target);
|
81 | }
|
82 | if(clobber)
|
83 | rmFile(target, function () {
|
84 | copyFile(file, target);
|
85 | });
|
86 | });
|
87 | }
|
88 |
|
89 | function copyFile(file, target) {
|
90 | var readStream = fs.createReadStream(file.name),
|
91 | writeStream = fs.createWriteStream(target, { mode: file.mode });
|
92 | if(transform) {
|
93 | transform(readStream, writeStream);
|
94 | } else {
|
95 | readStream.pipe(writeStream);
|
96 | }
|
97 | readStream.once('end', cb);
|
98 | }
|
99 |
|
100 | function rmFile(file, done) {
|
101 | fs.unlink(file, function (err) {
|
102 | if (err) {
|
103 | return onError(err);
|
104 | }
|
105 | return done();
|
106 | });
|
107 | }
|
108 |
|
109 | function onDir(dir) {
|
110 | var target = dir.name.replace(currentPath, targetPath);
|
111 | isWritable(target, function (writable) {
|
112 | if (writable) {
|
113 | return mkDir(dir, target);
|
114 | }
|
115 | copyDir(dir.name);
|
116 | });
|
117 | }
|
118 |
|
119 | function mkDir(dir, target) {
|
120 | fs.mkdir(target, dir.mode, function (err) {
|
121 | if (err) {
|
122 | return onError(err);
|
123 | }
|
124 | copyDir(dir.name);
|
125 | });
|
126 | }
|
127 |
|
128 | function copyDir(dir) {
|
129 | fs.readdir(dir, function (err, items) {
|
130 | if (err) {
|
131 | return onError(err);
|
132 | }
|
133 | items.forEach(function (item) {
|
134 | startCopy(dir + '/' + item);
|
135 | });
|
136 | return cb();
|
137 | });
|
138 | }
|
139 |
|
140 | function onLink(link) {
|
141 | var target = link.replace(currentPath, targetPath);
|
142 | fs.readlink(link, function (err, resolvedPath) {
|
143 | if (err) {
|
144 | return onError(err);
|
145 | }
|
146 | checkLink(resolvedPath, target);
|
147 | });
|
148 | }
|
149 |
|
150 | function checkLink(resolvedPath, target) {
|
151 | isWritable(target, function (writable) {
|
152 | if (writable) {
|
153 | return makeLink(resolvedPath, target);
|
154 | }
|
155 | fs.readlink(target, function (err, targetDest) {
|
156 | if (err) {
|
157 | return onError(err);
|
158 | }
|
159 | if (targetDest === resolvedPath) {
|
160 | return cb();
|
161 | }
|
162 | return rmFile(target, function () {
|
163 | makeLink(resolvedPath, target);
|
164 | });
|
165 | });
|
166 | });
|
167 | }
|
168 |
|
169 | function makeLink(linkPath, target) {
|
170 | fs.symlink(linkPath, target, function (err) {
|
171 | if (err) {
|
172 | return onError(err);
|
173 | }
|
174 | return cb();
|
175 | });
|
176 | }
|
177 |
|
178 | function isWritable(path, done) {
|
179 | fs.lstat(path, function (err, stats) {
|
180 | if (err) {
|
181 | if (err.code === 'ENOENT') return done(true);
|
182 | return done(false);
|
183 | }
|
184 | return done(false);
|
185 | });
|
186 | }
|
187 |
|
188 | function onError(err) {
|
189 | if (options.stopOnError) {
|
190 | return callback(err);
|
191 | }
|
192 | else if (!errs && options.errs) {
|
193 | errs = fs.createWriteStream(options.errs);
|
194 | }
|
195 | else if (!errs) {
|
196 | errs = [];
|
197 | }
|
198 | else if (options.errs) {
|
199 | if (typeof errs.write === 'undefined') {
|
200 | errs.push(err);
|
201 | }
|
202 | else {
|
203 | errs.write(err.stack + '\n\n');
|
204 | }
|
205 | }
|
206 | return cb();
|
207 | }
|
208 |
|
209 | function cb(skipped) {
|
210 | if (!skipped) running--;
|
211 | finished++;
|
212 | if ((started === finished) && (running === 0)) {
|
213 | return errs ? callback(errs) : callback(null);
|
214 | }
|
215 | }
|
216 | };
|
217 |
|
218 |
|