Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 545 lines (471 sloc) 23.433 kb
fb1747a @jlongster remove lots of legacy stuff, add require.js
jlongster authored
1 /*jslint regexp: true */
2 /*global define, console, process */
3
6b66bfc @dmose Get volo serve working in the new volofile syntax
dmose authored
4 var connect = require('connect'),
fb1747a @jlongster remove lots of legacy stuff, add require.js
jlongster authored
5 crypto = require('crypto'),
6 fs = require('fs'),
7 path = require('path'),
8 buildDir = 'www-built',
9 pagesDir = 'www-ghpages';
10
6b66bfc @dmose Get volo serve working in the new volofile syntax
dmose authored
11 module.exports = {
fb1747a @jlongster remove lots of legacy stuff, add require.js
jlongster authored
12 // Installs twitter bootstrap
13 install_twbootstrap: {
14 run: function (d, v, namedArgs, nowrap) {
15 var tempName = 'tempbootstrap',
16 jsNameRegExp = /bootstrap-(\w*)\.js$/;
17
18 function finished_message() {
19 console.log('\nTwitter Bootstrap installed.\n' +
20 'Add this css in your <head>:\n' +
21 '<link rel="stylesheet"' +
22 ' href="css/bootstrap.css">\n\n' +
23 'If you want the responsive styles, ' +
24 'add this also:\n' +
25 '<link rel="stylesheet"' +
26 ' href="css/bootstrap-responsive.css">\n');
27
28 if(nowrap) {
29 console.log('You also need to include any ' +
30 'js for components you use, like so:\n' +
31 '<script src="js/lib/bootstrap/button.js">' +
32 '</script>');
33 }
34 else {
35 // Double-quoting the require command is
36 // necessary so that require.js doesn't strip
37 // it out
38 console.log('You also need to install any ' +
39 'js components by just requiring ' +
40 'them, like so:\n' +
41 "require(\"bootstrap/button\");");
42 }
43 }
44
45 v.command('create', tempName, 'twitter/bootstrap')
46 .then(function () {
47 //Move the JS to the right location.
48 var jsFiles = v.getFilteredFileList(tempName + '/js', /\.js$/, /js\/tests\//),
49 promises = [];
50
51 jsFiles.forEach(function (file) {
52 //Pull off the name part from bootstrap-name.js pattern.
53 var match = jsNameRegExp.exec(file),
54 name, destName, damd;
55
56 if (!match) {
57 return;
58 }
59
60 name = jsNameRegExp.exec(file)[1];
61 destName = 'www/js/lib/bootstrap/' + name + '.js';
62 damd = q.defer();
63
64 v.copyFile(file, destName);
65
66 if(nowrap != 'nowrap') {
67 //Convert the file to AMD style
68 amdify.run.apply(amdify, [damd, v, {
69 depends: 'jquery'
70 }, destName]);
71 }
72
73 promises.push(damd);
74 });
75
76 //Wait for all the amdify calls to finish.
77 return q.all(promises);
78 })
79 .then(function () {
80 //Copy the images over.
81 v.copyDir(tempName + '/img', 'www/img');
82
83 //Copy the less files.
84 v.copyDir(tempName + '/less', 'tools/less');
85
86 //Compile the CSS.
87 return v.command('less').then(function () {
88 v.rm(tempName);
89 finished_message();
90 });
91 })
92 .then(d.resolve, d.reject);
93 }
94 },
95
96 install_xtags: {
97 run: function(d, v, namedArgs) {
98 var tempName = 'tempxtags',
99 jsNameRegExp = /\/([^\/]*)\.js$/;
100
101 function finished_message() {
102 console.log('x-tags is installed.\n' +
103 'You need to include the js and css for ' +
104 'each component you want to use, like so:\n\n' +
105 '<link rel="stylesheet"' +
106 ' href="css/lib/x-tag/accordion.css">\n' +
107 '<script src="js/lib/x-tag/accordion.js></script>');
108 }
109
110 v.command('create', tempName, 'csuwldcat/x-tag/gh-pages')
111 .then(function() {
112 //Move the JS to the right location.
113 v.copyFile(tempName + '/x-tag.js', 'www/js/lib/x-tag.js');
114
115 var jsFiles = v.getFilteredFileList(tempName + '/elements',
116 /\.js$/);
117 v.mkdir('www/js/lib/x-tag');
118 jsFiles.forEach(function(file) {
119 var matches = file.match(jsNameRegExp);
120 if(!matches) {
121 return;
122 }
123
124 var damd = q.defer();
125 var path = 'www/js/lib/x-tag/' + matches[1] + '.js';
126 v.copyFile(file, path);
127 });
128
129 //Copy the css
130 var files = v.getFilteredFileList(tempName + '/elements',
131 /\.css$/);
132 var basenameRegExp = /\/([^\/]*.css)/;
133
134 v.mkdir('www/css/lib/x-tag');
135 files.forEach(function(file) {
136 var matches = file.match(basenameRegExp);
137 if(!matches) {
138 return;
139 }
140
141 v.copyFile(file, 'www/css/lib/x-tag/' + matches[1]);
142 });
143
144 v.rm(tempName);
145
146 finished_message();
147 })
148 .then(d.resolve, d.reject);
149 }
150 },
151
152 //Builds the JS and CSS into one file each. If you want to do
153 //dynamic loading of scripts, pass -dynamic to the build, and
154 //require.js will be used to load scripts.
155 build: {
156 flags: {
157 //Does not print the build output.
158 'q': 'quiet',
159 //Uses dynamic loading via require.js instead of building
160 //all the modules in with almond.
161 'dynamic': 'dynamic'
162 },
163
164 run: function (d, v, namedArgs) {
9dcdac6 @dmose Fix 'volo build' and 'volo appcache' code by locally requiring the q …
dmose authored
165 var q = v.require('q');
fb1747a @jlongster remove lots of legacy stuff, add require.js
jlongster authored
166 q.call(function () {
167 //Remove the old dir
168 v.rm(buildDir);
169
170 if (!namedArgs.dynamic) {
171 //Copy the directory for output.
172 v.copyDir('www', buildDir);
173
174 //Remove the js dir from the built area, will be
175 //replaced by an optimized app.js
176 v.rm(buildDir + '/js');
177
178 //Do the CSS optimization
179 return v.spawn('node', ['tools/r.js', '-o',
180 'cssIn=www/css/app.css',
181 'out=' + buildDir + '/css/app.css'], {
182 useConsole: !namedArgs.quiet
183 });
184 }
185 return undefined;
186 })
187 .then(function () {
188 //JS go time
189 var optimize = namedArgs.optimize || 'uglify';
190
191 if (namedArgs.dynamic) {
192 //Still use require.js to load the app.js file.
193 return v.spawn('node', ['tools/r.js', '-o',
194 'appDir=www',
195 'baseUrl=js/lib',
196 'paths.app=../app',
197 'name=app',
198 'dir=' + buildDir,
199 'optimize=' + optimize], {
200 useConsole: !namedArgs.quiet
201 });
202 } else {
203 //The all-in-one option.
204 return v.spawn('node', ['tools/r.js', '-o',
205 'baseUrl=www/js/lib',
206 'paths.app=../app',
207 'paths.almond=../../../tools/almond',
208 'name=almond',
209 'include=app',
210 'out=' + buildDir + '/js/app.js',
211 'optimize=' + optimize], {
212 useConsole: !namedArgs.quiet
213 });
214 }
215 })
216 .then(function (buildOutput) {
217 //Remove all the CSS except for the app.css, since it
218 //inlines all the other ones.
219 v.getFilteredFileList(buildDir + '/css').forEach(function (path) {
220 if (!/app\.css$/.test(path)) {
221 v.rm(path);
222 }
223 });
224
1d4dcb1 @dperit Blew up mozilla marketplace references in app.js
dperit authored
225
226 v.copyFile(path.join('www', 'js', 'lib', 'modernizr-2.5.3.min.js'), path.join(buildDir, 'js', 'lib', 'modernizr-2.5.3.min.js'));
227 v.copyFile(path.join('www', 'js', 'lib', 'require.js'), path.join(buildDir, 'js', 'lib', 'require.js'));
228
fb1747a @jlongster remove lots of legacy stuff, add require.js
jlongster authored
229 //If almond is in use, it is built into app.js, so need
230 //to update the script tag to just load app.js instead.
231 if (!namedArgs.dynamic) {
232 var indexName = buildDir + '/index.html',
233 contents = v.read(indexName),
1d4dcb1 @dperit Blew up mozilla marketplace references in app.js
dperit authored
234 scriptRegExp = /(<script[^>]+data-main="[^"]+"[^>]+)(src="[^"]+")([^>]*>\s*<\/script>)/;
fb1747a @jlongster remove lots of legacy stuff, add require.js
jlongster authored
235
236 contents = contents.replace(scriptRegExp,
237 function (match, pre, script, post) {
238 return pre + 'src="js/app.js"' + post;
239 });
240
241 v.write(indexName, contents);
242 }
243 return buildOutput;
244 })
245 .then(function (buildOutput) {
246 d.resolve(buildOutput);
247 }, d.reject);
248 }
249 },
250
251 //Generates an SHA1 digest that represents the contents of the
252 //a directory. Call it like so: "volo digest dir=path/to/directory"
253 digest: {
254 validate: function (namedArgs) {
255 var dir = namedArgs.dir;
256 if (!dir) {
257 return new Error('Please specify a target directory for ' +
258 'the digest');
259 }
260 if (!path.existsSync(dir)) {
261 return new Error('Target directory for digest does ' +
262 'not exist: ' + dir);
263 }
264 return undefined;
265 },
266
267 run: function (d, v, namedArgs) {
9dcdac6 @dmose Fix 'volo build' and 'volo appcache' code by locally requiring the q …
dmose authored
268
269 var q = v.require('q');
270
fb1747a @jlongster remove lots of legacy stuff, add require.js
jlongster authored
271 var dir = namedArgs.dir,
272 files = v.getFilteredFileList(dir),
273 digests = [],
274 i = 0;
275
276 function getDigest(fileName) {
277 var shaSum = crypto.createHash('sha1'),
278 d = q.defer(),
279 stream = fs.ReadStream(fileName);
280
281 stream.on('data', function(data) {
282 shaSum.update(data);
283 });
284
285 stream.on('end', function() {
286 d.resolve(shaSum.digest('base64'));
287 });
288
289 return d.promise;
290 }
291
292 function digestFile(fileName) {
293 getDigest(fileName).then(function (digest) {
294 var shaSum;
295
296 digests[i] = digest;
297 i += 1;
298
299 if (i < files.length) {
300 digestFile(files[i]);
301 } else {
302 //All done, now generate the final digest,
303 //using the combination of the other digests
304 shaSum = crypto.createHash('sha1');
305 shaSum.update(digests.join(','));
306 d.resolve(shaSum.digest('base64'));
307 }
308 });
309 }
310
311 digestFile(files[0]);
312 }
313 },
314
315 ghdeploy: {
316 run: function (d, v, namedArgs) {
317 var spawnOptions = {
318 useConsole: !namedArgs.quiet
319 },
320 authInfo, repoName, hasGhPages;
321
b9b14b4 @dmose Fix ghdeploy to work again and to cleanup after itself
dmose authored
322 var q = v.require('q'),
323 github = v.require('volo/lib/github')
324 githubAuth = v.require('volo/lib/github/auth');
325
fb1747a @jlongster remove lots of legacy stuff, add require.js
jlongster authored
326 q.call(function () {
327 //First check if there is already a repo
328 if (!v.exists(buildDir)) {
329 throw new Error('Run build or appcache first to generate a deploy target in "www-built".');
330 }
331
332 //If already have gh-pages dir, go to next step.
333 if (v.exists(pagesDir)) {
334 return;
335 }
336
337 //Figure out if already in a github repo.
338 return githubAuth.fetch({ v: v })
339 .then(function (info) {
340 authInfo = info;
341
342 //Suggest the current directory name as the repo name.
343 repoName = path.basename(process.cwd());
344
345 return v.prompt(authInfo.user +
346 ', name of github repo [' +
347 repoName + ']:');
348 })
349 .then(function (promptRepoName) {
350 var dfd = q.defer();
351
352 if (promptRepoName) {
353 repoName = promptRepoName;
354 }
355
356 //First check to see if it exists.
357 github('repos/' + authInfo.user + '/' + repoName)
358 .then(function (data) {
359 var sshUrl = data.ssh_url;
360
b9b14b4 @dmose Fix ghdeploy to work again and to cleanup after itself
dmose authored
361 //Repo exists, see if there is a gh-pages branch
fb1747a @jlongster remove lots of legacy stuff, add require.js
jlongster authored
362 //already
363 github('repos/' + authInfo.user + '/' + repoName + '/branches')
364 .then(function (data) {
365 if (data && data.length) {
366 hasGhPages = data.some(function (branch) {
367 return branch.name === 'gh-pages';
368 });
369 }
370 dfd.resolve(sshUrl);
371 }, dfd.reject);
372 }, function (err) {
373 if (err.response.statusCode === 404) {
374 github('user/repos', {
375 method: 'POST',
376 token: authInfo.token,
377 content: {
378 name: repoName
379 }
380 })
381 .then(function (data) {
382 dfd.resolve(data.ssh_url);
383 }, function (err) {
384 dfd.reject(err);
385 });
386 } else {
387 dfd.reject(err);
388 }
389 });
390 return dfd.promise;
391 })
392 .then(function (sshUrl) {
393 //Set up .git.
394 v.mkdir(pagesDir);
395
396 //Set up the gh-pages repo in the built area.
397 return v.withDir(pagesDir, function () {
398 if (hasGhPages) {
399 //Set up the git repo locally. Just commit a file
400 //to get the repo prepped and sent to GitHub.
401 return v.sequence([
402 ['git', 'init'],
403 ['git', 'remote', 'add', 'origin', sshUrl],
404 //This step mandated by:
405 //http://help.github.com/pages/#project_pages_manually
406 ['git', 'symbolic-ref', 'HEAD', 'refs/heads/gh-pages'],
407 [v, 'rm', '.git/index'],
408 ['git', 'clean', '-fdx'],
409
410 ['git', 'pull', 'origin', 'gh-pages']
411 ], spawnOptions);
412 } else {
413 //Set up the git repo locally. Just commit a file
414 //to get the repo prepped and sent to GitHub.
415 return v.sequence([
416 ['git', 'init'],
417 ['git', 'remote', 'add', 'origin', sshUrl],
418 //This step mandated by:
419 //http://help.github.com/pages/#project_pages_manually
420 ['git', 'symbolic-ref', 'HEAD', 'refs/heads/gh-pages'],
421 [v, 'rm', '.git/index'],
422 ['git', 'clean', '-fdx'],
423
424 [v, 'write', 'index.html', 'Setting up pages...'],
425 ['git', 'add', 'index.html'],
426 ['git', 'commit', '-m', 'Create branch.'],
427 ['git', 'push', 'origin', 'gh-pages']
428 ], spawnOptions);
429 }
430 });
431 });
432 })
433 .then(function () {
434 var message = namedArgs.m;
435 if (!message) {
436 message = 'Deploy';
437 }
438
439 //Clean up www-ghpages first, but keep .git
440 if (v.exists(pagesDir)) {
441 fs.readdirSync(pagesDir).forEach(function (name) {
442 if (name !== '.git') {
443 v.rm(pagesDir + '/' + name);
444 }
445 });
446 }
447
448 //Copy the contents of www-built to www-ghpages
449 //Copy the directory for output.
450 v.copyDir(buildDir, pagesDir);
451
452 //Trigger update to origin.
453 return v.withDir(pagesDir, function () {
454 return v.sequence([
455 //Add any new files
456 ['git', 'add', '.'],
457 //Remove any files from git that are not on on disk
458 ['git', 'add', '-u'],
459 ['git', 'commit', '-m', message],
b9b14b4 @dmose Fix ghdeploy to work again and to cleanup after itself
dmose authored
460 ['git', 'push', 'origin', 'gh-pages'],
fb1747a @jlongster remove lots of legacy stuff, add require.js
jlongster authored
461 ], spawnOptions);
462 });
463 })
464 .then(function () {
b9b14b4 @dmose Fix ghdeploy to work again and to cleanup after itself
dmose authored
465 // return the main repo in the state it started in
466 v.sequence([['git', 'reset', 'HEAD']]);
467
fb1747a @jlongster remove lots of legacy stuff, add require.js
jlongster authored
468 if (repoName) {
469 return 'GitHub Pages is set up. Check http://' +
470 authInfo.user + '.github.com/' + repoName +
471 '/ in about 10-15 minutes.';
472 }
473 })
474 .then(d.resolve, d.reject);
475 }
476 },
477
478 //Runs less on the .less files in tools/less to generate the CSS files.
479 less: function (d, v, namedArgs) {
480 q.all([
481 v.exec('node tools/oneless.js tools/less/bootstrap.less > www/css/bootstrap.css'),
482 v.exec('node tools/oneless.js tools/less/responsive.less > www/css/bootstrap-responsive.css')
483 ])
484 .then(function () {
485 d.resolve();
486 })
487 .fail(d.reject);
488 },
489
490 appcache: function (d, v, namedArgs) {
491 var hasBuilt = v.exists(buildDir);
492
493 v.command('build')
494 .then(function () {
495 var manifest = v.read('tools/manifest.appcache'),
496 master = v.read(buildDir + '/index.html'),
497 appFiles;
498
1d4dcb1 @dperit Blew up mozilla marketplace references in app.js
dperit authored
499 appFiles = v.getFilteredFileList(buildDir, null, /\.htaccess/);
fb1747a @jlongster remove lots of legacy stuff, add require.js
jlongster authored
500 appFiles = appFiles.map(function (file) {
501 var start = file.indexOf('/' + buildDir + '/');
502 start = (start !== -1) ? (start + 11) : 0;
503 return file.substr(start, file.length);
504 });
505
506 master = master
507 .replace(/<html\s?/, '<html manifest="manifest.appcache" ')
508 .replace(/manifest\.appcache"\s>/, 'manifest.appcache">');
509 v.write(buildDir + '/index.html', master);
510
511 return v.command('digest', 'dir=' + buildDir)
512 .then(function (stamp) {
513 manifest = v.template(manifest, {
514 files : appFiles.join('\n'),
515 stamp : stamp
516 });
517 v.write(buildDir + '/manifest.appcache', manifest);
518 });
519 })
520 .then(function () {
521 //Inform the user of the right mime type, but only do it if
522 //there was not a previous build done.
523 d.resolve(hasBuilt ? '': 'Be sure to set the mime type for ' +
524 '.appcache files to be: text/cache-manifest');
525 })
526 .fail(d.reject);
6b66bfc @dmose Get volo serve working in the new volofile syntax
dmose authored
527 },
528
529 serve: function( d, v, namedArgs ) {
530 var port = 8086;
531 var base = process.cwd();
532
533 var middleware = [
534 connect.static( base ),
535 connect.directory( base )
536 ];
537 connect.logger.format( "WebGameStub", ("[D] server :method :url :status " +
538 ":res[content-length] - :response-time ms" ));
539 middleware.unshift( connect.logger( "WebGameStub" ) );
540
541 console.log( "starting web server on port " + port );
542 connect.apply( null, middleware ).listen( port );
fb1747a @jlongster remove lots of legacy stuff, add require.js
jlongster authored
543 }
544 };
Something went wrong with that request. Please try again.