This repository has been archived by the owner on Dec 2, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathindex.js
197 lines (155 loc) · 6.04 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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/* jshint node:true */
'use strict';
var through = require('through2'),
mkdirp = require('mkdirp'),
gutil = require('gulp-util'),
path = require('path'),
fs = require('fs'),
async = require('async'),
PluginError = gutil.PluginError,
File = gutil.File,
isWin = process.platform === 'win32',
debug;
var PLUGIN_NAME = 'gulp-symlink';
/**
* Wrapper to log when debug === true
* it's basically a console.log
*/
var log = function() {
if(debug === true) {
console.log.apply(console, [].slice.call(arguments));
return console.log;
} else {
return function() { };
}
};
/**
* Error wrapper - this is called in the through context
* @param {Error} error The error
* @return {Function} cb The through callback
*/
var errored = function(error, cb) {
this.emit('error', new PluginError(PLUGIN_NAME, error));
//Push the file so that the stream is piped to the next task even if it has errored
//might be discussed
this.push(this.source);
return cb();
};
var symlinker = function(destination, resolver, options) {
if(typeof resolver === 'object') {
options = resolver;
resolver = 'relative';
}
options = typeof options === 'object' ? options : {};
options.force = options.force === undefined ? false : options.force;
options.log = options.log === undefined ? true : options.log;
//Handling array of destinations, this test is because "instance of" isn't safe
if( Object.prototype.toString.call( destination ) === '[object Array]' ) {
//copy array because we'll shift values
var destinations = destination.slice();
}
return through.obj(function(source, encoding, callback) {
var self = this, symlink;
this.source = source; //error binding
//else if we've got an array from before take the next element as a destination path
symlink = destinations !== undefined ? destinations.shift() : symlink;
//if destination is a function pass the source to it
if(symlink === undefined) {
symlink = typeof destination === 'function' ? destination(source) : destination;
}
//if symlink is still undefined there is a problem!
if (symlink === undefined) {
return errored.call(self, 'An output destination is required.', callback);
}
// Convert the destination path to a new vinyl instance
symlink = symlink instanceof File ? symlink : new File({ path: symlink });
log('Before resolving')('Source: %s – dest: %s', source.path, symlink.path);
symlink.directory = path.dirname(symlink.path); //this is the parent directory of the symlink
// Resolve the path to the symlink
if(resolver === 'relative' || options.relative === true) {
source.resolved = path.relative(symlink.directory, source.path);
} else {
//resolve the absolute path from the source. It need to be from the current working directory to handle relative sources
source.resolved = path.resolve(source.cwd, source.path);
}
log('After resolving')(source.resolved + ' in ' + symlink.path);
fs.exists(symlink.path, function(exists) {
//No force option, we can't override!
if(exists && !options.force) {
return errored.call(self, 'Destination file exists ('+symlink.path+') - use force option to replace', callback);
} else {
async.waterfall([
function(next){
//remove destination if it exists already
if(exists && options.force === true) {
fs.unlink(symlink.path, function(err) {
if(err) {
return errored.call(self, err, callback);
}
next();
});
} else {
next();
}
},
//checking if the parent directory exists
function(next) {
mkdirp(symlink.directory, function(err) {
//ignoring directory err if it exists
if(err && err.code !== 'EEXIST') {
return errored.call(self, err, callback);
}
next();
});
}
], function () {
//this is a windows check as specified in http://nodejs.org/api/fs.html#fs_fs_symlink_srcpath_dstpath_type_callback
fs.stat(source.path, function(err, stat) {
if(err) {
return errored.call(self, err, callback);
}
source.stat = stat;
fs.symlink(source.resolved, symlink.path, source.stat.isDirectory() ? 'dir' : 'file', function(err) {
var success = function(){
if (options.log) {
gutil.log(PLUGIN_NAME + ':' + gutil.colors.magenta(source.path), 'symlinked to', gutil.colors.magenta(symlink.path));
}
self.push(source);
return callback();
};
if(err) {
if (!isWin || err.code !== 'EPERM') {
return errored.call(self, err, callback);
}
// Try with type "junction" on Windows
// Junctions behave equally to true symlinks and can be created in
// non elevated terminal (well, not always..)
fs.symlink(source.path, symlink.path, 'junction', function(err) {
if (err) {
return errored.call(self, err, callback);
}
return success();
});
}
return success();
});
});
});
}
});
});
};
var relativesymlinker = function(symlink, options) {
return symlinker(symlink, 'relative', options);
};
var absolutesymlinker = function(symlink, options) {
return symlinker(symlink, 'absolute', options);
};
// Expose main functionality under relative, for convenience
module.exports = relativesymlinker;
module.exports.relative = relativesymlinker;
module.exports.absolute = absolutesymlinker;
module.exports.File = File;
module.exports._setDebug = function(value) {
debug = value;
};