-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathindex.js
154 lines (135 loc) · 4.1 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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
"use strict";
var defaults = require("lodash/object/defaults");
var fs = require("fs-extra");
var path = require("path");
var chokidar = require("chokidar");
module.exports = function (source, target, opts, notify) {
opts = defaults(opts || {}, {
"watch": false,
"delete": false,
"depth": Infinity
});
if (typeof opts.depth !== "number" || isNaN(opts.depth)) {
notify("error", "Expected valid number for option 'depth'");
return false;
}
// Initial mirror
var mirrored = mirror(source, target, opts, notify, 0);
if (!mirrored) {
return false;
}
if (opts.watch) {
// Watcher to keep in sync from that
chokidar.watch(source, {
"persistent": true,
"depth": opts.depth,
"ignoreInitial": true
// TODO "ignore": opts.ignore
})
//.on("raw", console.log.bind(console, "raw"))
.on("ready", notify.bind(undefined, "watch", source))
.on("add", watcherCopy(source, target, opts, notify))
.on("addDir", watcherCopy(source, target, opts, notify))
.on("change", watcherCopy(source, target, opts, notify))
.on("unlink", watcherDestroy(source, target, opts, notify))
.on("unlinkDir", watcherDestroy(source, target, opts, notify))
.on("error", watcherError(opts, notify));
}
};
function watcherCopy (source, target, opts, notify) {
return function (f, stats) {
copy(f, path.join(target, path.relative(source, f)), notify);
};
}
function watcherDestroy (source, target, opts, notify) {
return function (f) {
deleteExtra(path.join(target, path.relative(source, f)), opts, notify);
};
}
function watcherError (opts, notify) {
return function (err) {
notify("error", err);
};
}
function mirror (source, target, opts, notify, depth) {
// Specifc case where the very source is gone
var sourceStat;
try {
sourceStat = fs.statSync(source);
} catch (e) {
// Source not found: destroy target?
if (fs.existsSync(target)) {
return deleteExtra(target, opts, notify);
}
}
var targetStat;
try {
targetStat = fs.statSync(target);
} catch (e) {
// Target not found? good, direct copy
return copy(source, target, notify);
}
if (sourceStat.isDirectory() && targetStat.isDirectory()) {
if (depth === opts.depth) {
notify("max-depth", source);
return true;
}
// copy from source to target
var copied = fs.readdirSync(source).every(function (f) {
return mirror(path.join(source, f), path.join(target, f), opts, notify, depth + 1);
});
// check for extraneous
var deletedExtra = fs.readdirSync(target).every(function (f) {
return fs.existsSync(path.join(source, f)) || deleteExtra(path.join(target, f), opts, notify);
});
return copied && deletedExtra;
} else if (sourceStat.isFile() && targetStat.isFile()) {
// compare update-time before overwriting
if (sourceStat.mtime > targetStat.mtime) {
return copy(source, target, notify);
} else {
return true;
}
} else if (opts.delete) {
// incompatible types: destroy target and copy
return destroy(target, notify) && copy(source, target, notify);
} else if (sourceStat.isFile() && targetStat.isDirectory()) {
// incompatible types
notify("error", "Cannot copy file '" + source + "' to '" + target + "' as existing folder");
return false;
} else if (sourceStat.isDirectory() && targetStat.isFile()) {
// incompatible types
notify("error", "Cannot copy folder '" + source + "' to '" + target + "' as existing file");
return false;
} else {
throw new Error("Unexpected case: WTF?");
}
}
function deleteExtra (fileordir, opts, notify) {
if (opts.delete) {
return destroy(fileordir, notify);
} else {
notify("no-delete", fileordir);
return true;
}
}
function copy (source, target, notify) {
notify("copy", [source, target]);
try {
fs.copySync(source, target);
return true;
} catch (e) {
notify("error", e);
return false;
}
}
function destroy (fileordir, notify) {
notify("remove", fileordir);
try {
fs.remove(fileordir);
return true;
} catch (e) {
notify("error", e);
return false;
}
}