1 | var path = require('path'),
|
2 | fs = require('fs'),
|
3 | exec = require('child_process').exec,
|
4 | async = require('async'),
|
5 | walk = require('./lib/walk');
|
6 |
|
7 | var defaultRegExcludes = [/^\.+.*/, /node_modules/]
|
8 |
|
9 | module.exports = exports = function(currentDir, originalFilePath, newFilePath, options, cb) {
|
10 | validatePaths(originalFilePath, newFilePath, function(err) {
|
11 | if (err) return cb(err);
|
12 |
|
13 | fs.stat(originalFilePath, function (err, stat) {
|
14 | if (err) return cb(err);
|
15 |
|
16 | if (stat.isDirectory()) {
|
17 | mvDir(currentDir, originalFilePath, newFilePath, options, cb)
|
18 | } else {
|
19 | mvFile(currentDir, originalFilePath, newFilePath, options, cb)
|
20 | }
|
21 | });
|
22 | })
|
23 | };
|
24 |
|
25 | function mvFile(currentDir, originalFilePath, newFilePath, options, cb) {
|
26 | steps = [function(cb) {
|
27 | rename(originalFilePath, newFilePath, options.git, cb)
|
28 | }];
|
29 |
|
30 | excludes = getExcludes(options);
|
31 | steps.push(function(cb) {updateReferencesInMovedFile(originalFilePath, newFilePath, null, cb)});
|
32 | steps.push(function(cb) {updateReferencesToMovedFile(currentDir, originalFilePath, newFilePath, excludes, cb)});
|
33 |
|
34 | async.series(steps, cb);
|
35 | }
|
36 |
|
37 | function getExcludes(options) {
|
38 | var excludes = defaultRegExcludes, steps = [];
|
39 | if (options.excludes) {
|
40 | excludes = excludes.concat(options.excludes);
|
41 | }
|
42 | return excludes;
|
43 | }
|
44 |
|
45 | function validatePaths(originalFilePath, newFilePath, cb) {
|
46 | async.series({
|
47 | srcExists: function(cb) {fs.exists(originalFilePath, function(exists) {cb(null, exists)})},
|
48 | destExists: function(cb) {fs.exists(newFilePath, function(exists) {cb(null, exists)})}
|
49 | }, function(err, result) {
|
50 | var srcExists = result.srcExists,
|
51 | destExists = result.destExists;
|
52 |
|
53 | if (!srcExists) return cb(new Error(originalFilePath + ' does not exist!'));
|
54 | if (destExists) return cb(new Error(newFilePath + ' is already exist!'));
|
55 | return cb(null, true);
|
56 | });
|
57 | }
|
58 |
|
59 | function mvDir(currentDir, originalDirPath, newDirPath, options, cb) {
|
60 | originalDirPath = originalDirPath.replace(/\/$/, '');
|
61 | if (newDirPath[newDirPath.length - 1] === '/') {
|
62 | newDirPath = newDirPath + path.basename(originalDirPath)
|
63 | }
|
64 |
|
65 | validatePaths(originalDirPath, newDirPath, function(err) {
|
66 | if (err) return cb(err);
|
67 |
|
68 | walk(originalDirPath, [], function(err, files) {
|
69 | if (err) return cb(err);
|
70 |
|
71 | rename(originalDirPath, newDirPath, options.git, function(err) {
|
72 | if (err) return cb(err);
|
73 |
|
74 | async.eachSeries(files, function(file, cb) {
|
75 | var newFilePath = file.replace(originalDirPath, newDirPath);
|
76 |
|
77 | steps = [];
|
78 | steps.push(function(cb) {
|
79 | var excludeRequires = '^' + originalDirPath;
|
80 | updateReferencesInMovedFile(file, newFilePath, new RegExp(excludeRequires), cb)
|
81 | });
|
82 | searchExcludes = getExcludes(options);
|
83 | steps.push(function(cb) {
|
84 | updateReferencesToMovedFile(currentDir, file, newFilePath, searchExcludes, cb)
|
85 | });
|
86 |
|
87 | async.parallel(steps, cb);
|
88 |
|
89 | }, cb)
|
90 | })
|
91 | })
|
92 | });
|
93 | }
|
94 |
|
95 | function rename(originalPath, newPath, supportGit, cb) {
|
96 | if (supportGit) {
|
97 | exec('git mv ' + originalPath + ' ' + newPath, function(err, stdout, stderr) {
|
98 | if (err) return cb(err);
|
99 | if (stderr) return cb(stderr);
|
100 | cb();
|
101 | })
|
102 | } else {
|
103 | fs.rename(originalPath, newPath, cb);
|
104 | }
|
105 | }
|
106 |
|
107 | function updateReferencesInMovedFile(originalFilePath, newFilePath, excludes, cb) {
|
108 | fs.readFile(newFilePath, 'utf8', function(err, data) {
|
109 | if (err) return cb(err);
|
110 |
|
111 | var requires = getRequires(data);
|
112 |
|
113 | if (requires) {
|
114 | requires.forEach(function(oldRequire) {
|
115 | var newRequire = generateNewRequire(oldRequire, originalFilePath, newFilePath, excludes);
|
116 | if (newRequire)
|
117 | data = data.replace(oldRequire, newRequire);
|
118 | })
|
119 | }
|
120 | fs.writeFile(newFilePath, data, {encoding: 'utf8'}, cb);
|
121 | });
|
122 | }
|
123 |
|
124 | function getRequires(fileData) {
|
125 | var re = /require(\(|\s)('|")(\.\S+)('|")(\))?/g;
|
126 | return fileData.match(re);
|
127 | }
|
128 |
|
129 | function generateNewRequire(oldRequire, originalFilePath, newFilePath, excludes) {
|
130 | var re = /require(\(|\s)('|")(\.\S+)('|")(\))?/,
|
131 | groups = re.exec(oldRequire),
|
132 | oldPath = groups[3],
|
133 | oldAsbPath = path.join(path.dirname(originalFilePath), oldPath);
|
134 | if (excludes && excludes.test(oldAsbPath)) return null;
|
135 | newRelativePath = path.relative(path.dirname(newFilePath), oldAsbPath);
|
136 |
|
137 | if (newRelativePath.indexOf('.') != 0 ) {
|
138 | newRelativePath = './' + newRelativePath;
|
139 | }
|
140 |
|
141 | return oldRequire.replace(re, 'require$1$2' + newRelativePath + '$4$5');
|
142 | }
|
143 |
|
144 | function updateReferencesToMovedFile(currentDir, originalFilePath, newFilePath, regExcludes, cb) {
|
145 | walk(currentDir, regExcludes, function(err, files) {
|
146 | if (err) return cb(err);
|
147 |
|
148 | function updateReferenceForFile(file, cb) {
|
149 | var oldRelativePath = path.relative(path.dirname(file), originalFilePath).replace(/\.(js|coffee)$/, ''),
|
150 | newRelativePath = path.relative(path.dirname(file), newFilePath).replace(/\.(js|coffee)$/, '');
|
151 | if (oldRelativePath.indexOf(".") != 0 ) {
|
152 | oldRelativePath = './' + oldRelativePath;
|
153 | }
|
154 | if (newRelativePath.indexOf(".") != 0 ) {
|
155 | newRelativePath = './' + newRelativePath;
|
156 | }
|
157 |
|
158 | var regex = exports.generateRequireRegex(oldRelativePath);
|
159 | fs.readFile(file, 'utf8', function(err, data) {
|
160 | if (err) return cb(err);
|
161 | if (data.indexOf(regex)) {
|
162 | var result = data.replace(regex, 'require$1$2' + newRelativePath + '$4$5');
|
163 | return fs.writeFile(file, result, {encoding: 'utf8'}, cb);
|
164 | } else {
|
165 | return cb()
|
166 | }
|
167 | })
|
168 | }
|
169 | async.eachSeries(files, updateReferenceForFile, cb);
|
170 | })
|
171 | }
|
172 |
|
173 | exports.generateRequireRegex = function(filePath) {
|
174 | return new RegExp("require(\\(|\\s)('|\")(" + filePath + ")('|\")(\\))?", "g");
|
175 | }
|