1 | var path = require('path');
|
2 | var fs = require('fs');
|
3 | var _ = require('underscore');
|
4 | var Dependencies = require('./dependencies/dependencies');
|
5 | var Config = require('./config');
|
6 | var Meteor = require('./meteor');
|
7 | var Command = require('./command');
|
8 | var wrench = require('wrench');
|
9 | var exec = require('child_process').exec;
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | Project = function(root, meteorArgs) {
|
16 |
|
17 |
|
18 | this.root = root;
|
19 | this.meteorArgs = meteorArgs;
|
20 | this.smartJsonPath = path.join(this.root, 'smart.json');
|
21 | this.smartLockPath = path.join(this.root, 'smart.lock');
|
22 | this.packagesRoot = path.join(this.root, 'packages');
|
23 |
|
24 |
|
25 | this.meteor = new Meteor(meteorArgs);
|
26 | };
|
27 |
|
28 |
|
29 | Project.prototype.initFromLock = function() {
|
30 | var self = this;
|
31 |
|
32 | if (fs.existsSync(this.smartLockPath)) {
|
33 | var data = fs.readFileSync(this.smartLockPath).toString();
|
34 | var lockData = JSON.parse(data);
|
35 |
|
36 |
|
37 | lockData.meteor.root = self.root;
|
38 | _.each(lockData.dependencies.basePackages, function(pkg) {
|
39 | pkg.root = self.root;
|
40 | });
|
41 | _.each(lockData.dependencies.packages, function(pkg) {
|
42 | pkg.root = self.root;
|
43 | });
|
44 |
|
45 | this.meteor = new Meteor(lockData.meteor);
|
46 | this.dependencies = Dependencies.newFromLockJson(lockData.dependencies);
|
47 | }
|
48 | };
|
49 |
|
50 |
|
51 | Project.prototype.checkSmartJson = function(forceUpdate) {
|
52 |
|
53 |
|
54 | var config = new Config(this.root);
|
55 |
|
56 | var newMeteor = new Meteor(config.meteor);
|
57 |
|
58 | if (config.name) {
|
59 | var specifier = {};
|
60 | specifier[config.name] = {path: "."};
|
61 | var newDeps = new Dependencies(specifier);
|
62 | } else {
|
63 | var newDeps = new Dependencies(config.packages);
|
64 | }
|
65 |
|
66 | if (forceUpdate || !this.meteor.equals(newMeteor) || !this.dependencies || !this.dependencies.equalBase(newDeps)) {
|
67 |
|
68 | if (!forceUpdate && this.dependencies)
|
69 | console.log('smart.json changed.. installing from smart.json');
|
70 |
|
71 | this.lockChanged = true;
|
72 | this.meteor = newMeteor;
|
73 | this.dependencies = newDeps;
|
74 | }
|
75 | };
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | Project.prototype.fetch = function(fn, forceUpdate) {
|
81 | var self = this;
|
82 |
|
83 |
|
84 | self.initFromLock();
|
85 | self.checkSmartJson(forceUpdate);
|
86 |
|
87 |
|
88 | var buildDevBundle = !! self.meteorArgs['build-dev-bundle'];
|
89 | self.meteor.prepare(buildDevBundle, function() {
|
90 |
|
91 |
|
92 | if (!self.dependencies.resolved()) {
|
93 |
|
94 | console.verbose('Resolving dependency tree');
|
95 |
|
96 | self.dependencies.resolve(self.meteorArgs.force, function(err, conflicts) {
|
97 |
|
98 | _.each(conflicts, function(conflict, name) {
|
99 | console.log(('Problem installing ' + name.bold).red);
|
100 | console.log((" ✘ " + conflict).red);
|
101 | });
|
102 |
|
103 | if (err) {
|
104 | console.log(err.message.red);
|
105 | process.exit();
|
106 | }
|
107 |
|
108 | fn();
|
109 | });
|
110 | } else {
|
111 | fn();
|
112 | }
|
113 | });
|
114 | };
|
115 |
|
116 | Project.prototype.uninstall = function() {
|
117 | var self = this;
|
118 |
|
119 |
|
120 | if (fs.existsSync(this.packagesRoot)) {
|
121 | var dirs = fs.readdirSync(this.packagesRoot);
|
122 |
|
123 | _.each(dirs, function(dir) {
|
124 | var dirPath = path.join(self.packagesRoot, dir);
|
125 |
|
126 | if (fs.lstatSync(dirPath).isSymbolicLink())
|
127 | fs.unlink(dirPath)
|
128 | });
|
129 | }
|
130 | };
|
131 |
|
132 |
|
133 | Project.prototype.install = function(fn, forceUpdate) {
|
134 | var self = this;
|
135 |
|
136 | self._optimizeFS();
|
137 |
|
138 |
|
139 | self.fetch(function() {
|
140 |
|
141 |
|
142 | if (! self.dependencies.isEmpty())
|
143 | wrench.mkdirSyncRecursive(self.packagesRoot);
|
144 |
|
145 |
|
146 | self.dependencies.installInto(self, function(packagesInstalled) {
|
147 | console.log();
|
148 | console.log('Done installing smart packages'.bold);
|
149 |
|
150 |
|
151 | if (fs.existsSync(self.smartJsonPath) && (self.lockChanged || !fs.existsSync(self.smartLockPath)))
|
152 | self.writeLockFile();
|
153 |
|
154 | fn();
|
155 | });
|
156 | }, forceUpdate);
|
157 | };
|
158 |
|
159 |
|
160 | Project.prototype.needsToInstall = function() {
|
161 | return !this.dependencies.isEmpty();
|
162 | }
|
163 |
|
164 |
|
165 | Project.prototype.update = function(fn) {
|
166 |
|
167 | this.install(fn, true);
|
168 | };
|
169 |
|
170 | Project.prototype.execute = function(args, fn) {
|
171 | var self = this;
|
172 |
|
173 | if (self.meteorArgs.version)
|
174 | console.suppress();
|
175 |
|
176 | console.log();
|
177 | console.log("Stand back while Meteorite does its thing".bold);
|
178 |
|
179 |
|
180 |
|
181 | self.install(function() {
|
182 |
|
183 | console.log();
|
184 | console.log("Ok, everything's ready. Here comes Meteor!".green);
|
185 | console.log();
|
186 |
|
187 | if (self.meteorArgs.version)
|
188 | console.unsuppress();
|
189 |
|
190 | self.meteor.execute(args, self.packagesRoot, fn);
|
191 | });
|
192 | };
|
193 |
|
194 |
|
195 | Project.prototype.isUsing = function(packageName, fn) {
|
196 | var self = this;
|
197 |
|
198 | self.install(function() {
|
199 | return self.meteor.isUsing(packageName, fn);
|
200 | });
|
201 | }
|
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 | Project.prototype.installPackage = function(pkgName, version, fn) {
|
211 | var self = this;
|
212 |
|
213 |
|
214 | self.fetch(function() {
|
215 |
|
216 | self.hasPackage(pkgName, function(check) {
|
217 |
|
218 | if (check)
|
219 | return fn();
|
220 |
|
221 |
|
222 | Atmosphere.package(pkgName, function(atmosphere_defn) {
|
223 |
|
224 | if (!atmosphere_defn)
|
225 | throw("Package named " + pkgName + " doesn't exist in your meteor installation, smart.json, or on atmosphere");
|
226 |
|
227 |
|
228 |
|
229 | var smartJson = self.readSmartJson();
|
230 | var defn = {}
|
231 | if (version)
|
232 | defn.version = version;
|
233 | smartJson.packages = smartJson.packages || {};
|
234 | smartJson.packages[pkgName] = defn;
|
235 | self.writeSmartJson(smartJson);
|
236 |
|
237 |
|
238 | self.checkSmartJson(true);
|
239 |
|
240 | fn();
|
241 | });
|
242 | });
|
243 | });
|
244 | }
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 | Project.prototype.hasPackage = function(pkgName, fn) {
|
251 | if (this.dependencies.packages[pkgName]) {
|
252 | return fn(true);
|
253 | }
|
254 |
|
255 | this.meteor.hasPackage(pkgName, fn)
|
256 | };
|
257 |
|
258 |
|
259 | Project.prototype.readSmartJson = function() {
|
260 |
|
261 | try {
|
262 | var rawConfig = fs.readFileSync(path.join(this.root, 'smart.json')).toString();
|
263 | return JSON.parse(rawConfig);
|
264 |
|
265 | } catch (err) {
|
266 | return {};
|
267 | }
|
268 | };
|
269 |
|
270 | Project.prototype.smartJson = function() {
|
271 | var data = {};
|
272 |
|
273 | if (!this.meteor.defaultMeteor)
|
274 | data.meteor = this.meteor.toJson();
|
275 |
|
276 | if (this.dependencies)
|
277 | data.packages = this.dependencies.toJson().basePackages;
|
278 | else
|
279 | data.packages = {};
|
280 |
|
281 | return data;
|
282 | };
|
283 |
|
284 |
|
285 | Project.prototype.writeSmartJson = function(json) {
|
286 | json = json || this.smartJson();
|
287 |
|
288 |
|
289 | var smartJsonString = JSON.stringify(json, null, 2) + "\n";
|
290 |
|
291 |
|
292 | if (fs.existsSync(this.root))
|
293 | fs.writeFileSync(this.smartJsonPath, smartJsonString);
|
294 | };
|
295 |
|
296 | Project.prototype.lockJson = function() {
|
297 |
|
298 | return {
|
299 | meteor: this.meteor.toJson(true),
|
300 | dependencies: this.dependencies.toJson(true)
|
301 | };
|
302 | };
|
303 |
|
304 |
|
305 | Project.prototype.writeLockFile = function() {
|
306 |
|
307 | var smartJsonString = JSON.stringify(this.lockJson(), null, 2) + "\n";
|
308 | fs.writeFileSync(this.smartLockPath, smartJsonString);
|
309 | };
|
310 |
|
311 | Project.prototype._optimizeFS = function() {
|
312 | var self = this;
|
313 |
|
314 | var deletable = [];
|
315 |
|
316 |
|
317 | var oldInstallRoot = path.join(this.root, '.meteor', 'meteorite')
|
318 | if (fs.existsSync(oldInstallRoot)) {
|
319 | deletable.push(oldInstallRoot);
|
320 | }
|
321 |
|
322 | if (deletable.length > 0)
|
323 | console.log("Yay! We're optimizing your installation!".yellow.bold);
|
324 |
|
325 | _.each(deletable, function(filePath) {
|
326 | console.log(" ✘ ".red + ("Deleting " + filePath).grey);
|
327 |
|
328 | if (fs.lstatSync(filePath).isDirectory())
|
329 | wrench.rmdirSyncRecursive(filePath);
|
330 | else
|
331 | fs.unlink(filePath);
|
332 | });
|
333 | };
|
334 |
|
335 | module.exports = Project;
|
336 |
|
337 |
|
338 |
|