Rebuild examples when the sources change

This commit is contained in:
Tim Schaub
2015-04-12 14:42:32 -06:00
parent aaba58ceb0
commit 8ddec98075
5 changed files with 55 additions and 192 deletions

View File

@@ -46,7 +46,9 @@
"devDependencies": {
"clean-css": "2.2.16",
"coveralls": "2.11.2",
"debounce": "^1.0.0",
"expect.js": "0.3.1",
"gaze": "^0.5.1",
"istanbul": "0.3.13",
"jquery": "2.1.1",
"jshint": "2.5.6",
@@ -54,9 +56,9 @@
"mocha-phantomjs": "3.5.1",
"phantomjs": "1.9.10",
"proj4": "2.3.3",
"resemblejs": "1.2.0",
"sinon": "1.10.3",
"slimerjs-edge": "0.10.0-pre-2",
"resemblejs": "1.2.0"
"slimerjs-edge": "0.10.0-pre-2"
},
"ext": [
"rbush"

View File

@@ -1,17 +1,12 @@
var async = require('async');
var buildExt = require('./build-ext');
var parseExamples = require('./parse-examples');
/**
* Parse examples and build external modules.
*/
async.waterfall([
parseExamples,
buildExt
], function(err) {
buildExt(function(err) {
if (err) {
process.stderr.write(err + '\n');
process.exit(1);
} else {
process.exit(0);
}
});

View File

@@ -1,163 +0,0 @@
var fs = require('fs');
var path = require('path');
var async = require('async');
var Parser = require('htmlparser2').Parser;
var buildExamples = require('./build-examples');
var exampleDir = path.join(__dirname, '..', 'examples');
/**
* List all .html files in the example directory (excluding index.html).
* @param {function(Error, Array.<string>)} callback Called with any error or
* the list of paths to examples.
*/
function listExamples(callback) {
fs.readdir(exampleDir, function(err, items) {
if (err) {
return callback(err);
}
var examplePaths = items.filter(function(item) {
return /\.html$/i.test(item) && item !== 'index.html';
}).map(function(item) {
return path.join(exampleDir, item);
});
callback(null, examplePaths);
});
}
/**
* Parse info from examples.
* @param {Array.<string>} examplePaths Paths to examples.
* @param {function(Error, Array.<Object>)} callback Called with any error or
* the list of example info objects.
*/
function parseExamples(examplePaths, callback) {
async.map(examplePaths, function(examplePath, next) {
fs.readFile(examplePath, function(err, data) {
if (err) {
return next(err);
}
var name = path.basename(examplePath);
var info = {
link: name,
example: name,
title: '',
shortdesc: '',
tags: ''
};
var key;
var openTag;
var parser = new Parser({
onopentag: function(tag, attrs) {
if (attrs.id in info) {
key = attrs.id;
openTag = tag;
}
},
ontext: function(text) {
if (key) {
info[key] += text.replace(/\n/g, '').trim() + ' ';
}
},
onclosetag: function(tag) {
if (tag === openTag) {
info[key] = info[key].trim();
key = undefined;
openTag = undefined;
}
},
onerror: function(err2) {
var message = 'Trouble parsing ' + examplePath + '\n' + err2.message;
next(new Error(message));
}
});
parser.write(data.toString('utf8'));
parser.end();
next(null, info);
});
}, callback);
}
/**
* Create an inverted index of keywords from examples. Property names are
* lowercased words. Property values are objects mapping example index to word
* count.
* @param {Array.<Object>} exampleInfos Array of example info objects.
* @return {Object} Word index.
*/
function createWordIndex(exampleInfos) {
var index = {};
var keys = ['shortdesc', 'title', 'tags'];
exampleInfos.forEach(function(info, i) {
keys.forEach(function(key) {
var text = info[key];
var words = text ? text.split(/\W+/) : [];
words.forEach(function(word) {
if (word) {
word = word.toLowerCase();
var counts = index[word];
if (counts) {
if (index in counts) {
counts[i] += 1;
} else {
counts[i] = 1;
}
} else {
counts = {};
counts[i] = 1;
index[word] = counts;
}
}
});
});
});
return index;
}
/**
* Write the example-list.js file with example info and word index.
* @param {Array.<Object>} exampleInfos Array of example info objects.
* @param {function(Error)} callback Called with any error.
*/
function writeExampleList(exampleInfos, callback) {
var info = {
examples: exampleInfos,
index: createWordIndex(exampleInfos)
};
var indexPath = path.join(exampleDir, 'example-list.js');
var str = 'var info = ' + JSON.stringify(info);
fs.writeFile(indexPath, str, callback);
}
/**
* List examples, parse them, and write example list.
* @param {function(Error)} callback Called with any error.
*/
function main(callback) {
async.waterfall([
buildExamples,
listExamples,
parseExamples,
writeExampleList
], callback);
}
if (require.main === module) {
main(function(err) {
if (err) {
process.stderr.write(err.message + '\n');
process.exit(1);
} else {
process.exit(0);
}
});
}
module.exports = main;

View File

@@ -107,13 +107,10 @@ This is useful for projects that use the Closure Compiler to build, but want to
Called internally to parse the library for annotations and write out a `build/info.json` file.
## `parse-examples.js`
Called after install to generate an example index. After new examples are added, run `node tasks/parse-examples.js` to regenerate the example index.
## `build-examples.js`
Called internally by `parse-examples.js` to build the examples from templates.
Builds examples and the example index.
## `serve.js`

View File

@@ -7,9 +7,12 @@
var path = require('path');
var url = require('url');
var Gaze = require('gaze').Gaze;
var closure = require('closure-util');
var debounce = require('debounce');
var nomnom = require('nomnom');
var buildExamples = require('./build-examples');
var log = closure.log;
@@ -68,6 +71,26 @@ var createServer = exports.createServer = function(callback) {
});
};
/**
* Build the examples and exit on any error.
* @param {Function=} opt_callback Called when done building examples.
*/
function buildExamplesOrFatal(opt_callback) {
log.info('serve', 'Building examples.');
buildExamples(function(err) {
if (err) {
log.error('serve', 'Building examples failed.');
log.error('serve', err.message);
log.error('serve', 'Use "verbose" logging to see the full stack trace.');
log.verbose('serve', err.stack);
process.exit(1);
}
log.verbose('serve', 'Done building examples.');
if (opt_callback) {
opt_callback();
}
});
}
/**
* If running this module directly start the server.
@@ -92,21 +115,30 @@ if (require.main === module) {
/** @type {string} */
log.level = options.loglevel;
log.info('serve', 'Parsing dependencies ...');
createServer(function(err, server) {
if (err) {
log.error('serve', 'Parsing failed');
log.error('serve', err.message);
process.exit(1);
}
server.listen(options.port, function() {
log.info('serve', 'Listening on http://localhost:' +
options.port + '/ (Ctrl+C to stop)');
});
server.on('error', function(err) {
log.error('serve', 'Server failed to start: ' + err.message);
process.exit(1);
buildExamplesOrFatal(function() {
log.info('serve', 'Parsing dependencies.');
createServer(function(err, server) {
if (err) {
log.error('serve', 'Parsing failed');
log.error('serve', err.message);
process.exit(1);
}
server.listen(options.port, function() {
log.info('serve', 'Listening on http://localhost:' +
options.port + '/ (Ctrl+C to stop)');
});
server.on('error', function(err) {
log.error('serve', 'Server failed to start: ' + err.message);
process.exit(1);
});
});
var gaze = new Gaze('examples_src/**/*');
var debouncedBuild = debounce(buildExamplesOrFatal, 250);
gaze.on('all', function(event, filepath) {
log.verbose('serve', 'Watch event: ' + event + ' ' + filepath);
debouncedBuild();
});
});
}