Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 10 commits
  • 15 files changed
  • 0 commit comments
  • 1 contributor
View
616 docs/anvil.html
345 additions, 271 deletions not shown
View
351 lib/anvil.js
@@ -20,7 +20,7 @@
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*---------------------------------------------------------------------------*/
-var Anvil, ArgParser, Combiner, Compiler, Configuration, Context, Continuous, Documenter, FSCrawler, FSProvider, Host, Log, MarkupPipeline, MochaRunner, PostProcessor, Runner, Scheduler, SocketServer, SourcePipeline, StylePipeline, Suite, ape, coffeeKup, coffeeScript, colors, config, continuous, cssminifier, debug, defaultDoc, defaultMocha, docco, emitter, events, express, ext, extensionLookup, fs, haml, inProcess, interfaces, jslint, jsp, less, libConfig, log, marked, mkdir, mocha, path, pro, quiet, reporters, siteConfig, stylus, test, version, _;
+var Anvil, Cli, Combiner, Commander, Compiler, Configuration, Context, Continuous, Documenter, FSCrawler, FSProvider, Host, Log, MarkupPipeline, MochaRunner, PostProcessor, Runner, Scheduler, SocketServer, SourcePipeline, StylePipeline, Suite, ape, coffeeKup, coffeeScript, colors, config, continuous, cssminifier, debug, defaultDoc, defaultMocha, docco, emitter, events, express, ext, extensionLookup, fs, haml, inProcess, interfaces, jslint, jsp, less, libConfig, log, marked, mkdir, mocha, path, pro, quiet, reporters, siteConfig, stylus, test, _;
events = require("events");
emitter = events.EventEmitter;
_ = require("underscore");
@@ -28,7 +28,6 @@ colors = require("colors");
fs = require("fs");
mkdir = require("mkdirp").mkdirp;
path = require("path");
-ArgParser = require("argparser");
express = require("express");
Log = (function() {
function Log() {}
@@ -54,6 +53,7 @@ log = new Log();
exports.log = log;
_ = require("underscore");
path = require("path");
+Commander = require("Commander").Command;
config = {};
siteConfig = {
"source": "src",
@@ -96,7 +96,6 @@ defaultDoc = {
output: "docs"
};
continuous = test = inProcess = quiet = debug = false;
-version = "0.7.3";
ext = {
gzip: "gz",
uglify: "min",
@@ -115,68 +114,54 @@ extensionLookup = {
".html": "markup"
};
Configuration = (function() {
- function Configuration(fp, parser, scheduler, log) {
+ function Configuration(fp, scheduler, log) {
this.fp = fp;
- this.parser = parser;
this.scheduler = scheduler;
this.log = log;
}
- Configuration.prototype.configure = function(onConfig) {
- var buildFile, buildOpt, createLibFile, createSiteFile, exists, host, libScaffold, name, runApe, runDocco, scaffold, self, showVersion, siteScaffold, type, useMocha;
+ Configuration.prototype.configure = function(argList, onConfig) {
+ var buildFile, command, exists, name, scaffold, self, type;
self = this;
- this.parser.addValueOptions(["b", "build", "n", "html", "site", "lib", "libfile", "sitefile"]);
- this.parser.parse();
- buildOpt = this.parser.getOptions("b", "build");
- buildFile = buildOpt ? buildOpt : "./build.json";
- createLibFile = this.parser.getOptions("libfile");
- createSiteFile = this.parser.getOptions("sitefile");
- continuous = this.parser.getOptions("ci");
- host = this.parser.getOptions("h", "host");
- useMocha = this.parser.getOptions("mocha");
- libScaffold = this.parser.getOptions("lib");
- quiet = this.parser.getOptions("q", "quiet");
- showVersion = this.parser.getOptions("v", "version");
- siteScaffold = this.parser.getOptions("site");
- runDocco = this.parser.getOptions("docco");
- runApe = this.parser.getOptions("ape");
- if (showVersion) {
- this.log.onEvent("Anvil.js " + version);
- return onConfig(config, true);
- } else if (createLibFile || createSiteFile) {
- name = createLibFile || (createLibFile = createSiteFile);
- type = createSiteFile ? 'site' : 'lib';
+ command = new Commander();
+ command.version("0.7.4").option("-b, --build [build file]", "Use a custom build file", "./build.json").option("--ci", "Run a continuous integration build").option("--host", "Setup a static HTTP host").option("--lib [project]", "Create a lib project at the folder [project]").option("--libfile [file name]", "Create a new lib build file named [file name]").option("--site [project]", "Create a site project at the folder [project]").option("--sitefile [file name]", "Create a new site build file named [file name]").option("--mocha", "Run specifications using Mocha").option("--docco", "Create annotated source using docco").option("--ape", "Create annotated source using ape").option("-q, --quiet", "Only print completion and error messages");
+ command.parse(argList);
+ if (command.libfile || command.sitefile) {
+ name = command.libfile || (command.libfile = command.sitefile);
+ type = command.sitefile ? 'site' : 'lib';
return this.writeConfig(type, "" + name + ".json", function() {
+ self.log.onComplete("Created " + type + " build file - " + name);
return onConfig(config, true);
});
- } else if (siteScaffold || libScaffold) {
- type = siteScaffold ? 'site' : 'lib';
- scaffold = siteScaffold || (siteScaffold = libScaffold);
+ } else if (command.site || command.lib) {
+ type = command.site ? 'site' : 'lib';
+ scaffold = command.site || (command.site = command.lib);
config = type === 'site' ? siteConfig : libConfig;
this.log.onStep("Creating scaffolding for new " + type + " project");
return self.ensurePaths(function() {
return self.writeConfig(type, scaffold + "/build.json", function() {
- self.log.onComplete("Scaffold " + scaffold + " created!");
+ self.log.onComplete("Scaffold ( " + scaffold + " ) created.");
return onConfig(config, true);
});
}, scaffold);
} else {
- this.log.onStep("Checking for config...");
+ buildFile = command.build;
+ this.log.onStep("Checking for " + buildFile);
exists = this.fp.pathExists(buildFile);
return this.prepConfig(exists, buildFile, function() {
- if (host) {
+ if (command.host) {
config.host = true;
}
- if (continuous) {
+ if (command.ci) {
config.continuous = true;
}
- if (useMocha) {
+ if (command.mocha) {
config.mocha = defaultMocha;
}
- if (runApe) {
+ if (command.ape) {
config.docs = defaultDoc;
config.docs.generator = "ape";
}
- if (runDocco) {
+ if (command.docco) {
config.docs = defaultDoc;
}
return self.ensurePaths(function() {
@@ -246,6 +231,7 @@ Configuration = (function() {
return done();
}
};
+ this.log.onStep("Ensuring project directory structure");
return this.scheduler.parallel(paths, worker, function() {
return self.copyPrereqs(onComplete);
});
@@ -276,9 +262,10 @@ Configuration = (function() {
});
};
Configuration.prototype.loadConvention = function(onComplete) {
- var conventionConfig;
- conventionConfig = this.fp.pathExists("./site") ? siteConfig : libConfig;
- this.log.onStep("Loading convention...");
+ var conventionConfig, isSite;
+ isSite = this.fp.pathExists("./site");
+ conventionConfig = isSite ? siteConfig : libConfig;
+ this.log.onStep("No build file found, using " + (isSite ? 'site' : 'lib') + " conventions");
config = conventionConfig;
return onComplete();
};
@@ -764,6 +751,7 @@ Compiler = (function() {
newExt = this.extensionMap[ext];
newFile = file.name.replace(ext, newExt);
log = this.log;
+ log.onEvent("Compiling " + file.name + " to " + newFile);
compiler = this.compilers[ext];
if (compiler) {
return this.fp.transform([file.workingPath, file.name], compiler, [file.workingPath, newFile], function(err) {
@@ -771,6 +759,7 @@ Compiler = (function() {
file.name = newFile;
return onComplete(file);
} else {
+ log.onError("Error compiling " + file.name + ": \r\n " + err);
return onComplete(err);
}
});
@@ -1062,7 +1051,11 @@ StylePipeline = (function() {
});
}
return forAll(files, self.finalize, function() {
+ self.log.onStep("Finalizing CSS");
return forAll(minified, self.minify, function() {
+ if (minified.length > 0) {
+ self.log.onStep("Minifying CSS");
+ }
return forAll(minified, self.finalize, function() {
return onComplete(files.concat(minified));
});
@@ -1073,6 +1066,7 @@ StylePipeline = (function() {
StylePipeline.prototype.minify = function(file, onComplete) {
var newFile, self;
if (this.config.cssmin) {
+ this.log.onEvent("Minifying " + file.name);
self = this;
ext = file.ext();
newFile = file.name.replace(ext, ".min.css");
@@ -1090,6 +1084,7 @@ StylePipeline = (function() {
var footer, header, self;
self = this;
if (this.config.finalize && this.config.finalize.style) {
+ this.log.onEvent("Finalizing " + file.name);
header = this.config.finalize.style.header;
footer = this.config.finalize.style.footer;
return this.fp.transform([file.workingPath, file.name], function(content, onTransform) {
@@ -1109,6 +1104,7 @@ StylePipeline = (function() {
var prefix, self, suffix;
self = this;
if (this.config.wrap && this.config.wrap.style) {
+ this.log.onEvent("Wrapping " + file.name);
prefix = this.config.wrap.style.prefix;
suffix = this.config.wrap.style.suffix;
return this.fp.transform([file.workingPath, file.name], function(content, onTransform) {
@@ -1148,7 +1144,11 @@ SourcePipeline = (function() {
});
}
return forAll(files, self.finalize, function() {
+ self.log.onStep("Finalizing source files");
return forAll(minify, self.minify, function() {
+ if (minify.length > 0) {
+ self.log.onStep("Minifying source files");
+ }
return forAll(minify, self.finalize, function() {
return onComplete(files.concat(minify));
});
@@ -1162,7 +1162,7 @@ SourcePipeline = (function() {
self = this;
ext = file.ext();
newFile = file.name.replace(ext, ".min.js");
- this.log.onStep("Uglifying " + newFile);
+ this.log.onEvent("Minifying " + newFile);
return this.fp.transform([file.workingPath, file.name], function(content, onTransform) {
return self.minifier(content, function(err, result) {
if (err) {
@@ -1183,7 +1183,7 @@ SourcePipeline = (function() {
var footer, header, self;
self = this;
if (this.config.finalize && this.config.finalize.source) {
- this.log.onStep("Finalizing " + file.name);
+ this.log.onEvent("Finalizing " + file.name);
header = this.config.finalize.source.header;
footer = this.config.finalize.source.footer;
return this.fp.transform([file.workingPath, file.name], function(content, onTransform) {
@@ -1205,7 +1205,7 @@ SourcePipeline = (function() {
var prefix, self, suffix;
self = this;
if (this.config.wrap && this.config.wrap.source) {
- this.log.onStep("Wrapping " + file.name);
+ this.log.onEvent("Wrapping " + file.name);
prefix = this.config.wrap.source.prefix;
suffix = this.config.wrap.source.suffix;
return this.fp.transform([file.workingPath, file.name], function(content, onTransform) {
@@ -1288,9 +1288,12 @@ Documenter = (function() {
Documenter.prototype.generate = function(files) {
var self;
self = this;
- return this.scheduler.parallel(files, this.document, function() {
- return self.log.onComplete("Documents have been completed");
- });
+ if (files && files.length > 0) {
+ this.log.onEvent("Creating annotated source for: " + (_.pluck(files, 'name').toString()));
+ return this.scheduler.parallel(files, this.document, function() {
+ return self.log.onComplete("Code annotation completed");
+ });
+ }
};
Documenter.prototype.document = function(file, onComplete) {
var language, newFile, self;
@@ -1298,7 +1301,7 @@ Documenter = (function() {
language = ape.get_language(file.name);
ext = file.ext();
newFile = file.name.replace(ext, ".html");
- this.log.onStep("Generating source documentation for " + file.name);
+ this.log.onEvent("Annotation for " + file.name);
return this.fp.read([file.workingPath, file.name], function(content) {
return self.generator(language, ext, newFile, content, function(doc) {
return self.fp.write([self.config.docs.output, newFile], doc, onComplete);
@@ -1318,8 +1321,7 @@ Documenter = (function() {
return Documenter;
})();
Anvil = (function() {
- function Anvil(config, fp, compiler, combiner, documenter, scheduler, postProcessor, log, callback) {
- this.config = config;
+ function Anvil(fp, compiler, combiner, documenter, scheduler, postProcessor, log, callback) {
this.fp = fp;
this.compiler = compiler;
this.combiner = combiner;
@@ -1328,29 +1330,14 @@ Anvil = (function() {
this.postProcessor = postProcessor;
this.log = log;
this.callback = callback;
- config = this.config;
- this.filesBuilt = {};
+ this.buildNumber = 0;
this.inProcess = false;
- this.steps = {
- source: false,
- style: false,
- markup: false,
- hasSource: config.source,
- hasStyle: config.style,
- hasMarkup: config.markup,
- markupReady: function() {
- return (this.source || !this.hasSource) && (this.style || !this.hasStyle);
- },
- allDone: function() {
- var status;
- status = (this.source || !this.hasSource) && (this.style || !this.hasStyle) && (this.markup || !this.hasMarkup);
- return status;
- }
- };
}
Anvil.prototype.extensions = [".js", ".coffee", ".html", ".haml", ".markdown", ".md", ".css", ".styl", ".less", ".css"];
- Anvil.prototype.build = function() {
+ Anvil.prototype.build = function(config) {
if (!this.inProcess) {
+ this.initialize(config);
+ this.log.onStep("Build " + this.buildNumber + " initiated");
this.inProcess = true;
this.buildSource();
return this.buildStyle();
@@ -1374,6 +1361,26 @@ Anvil = (function() {
replacePatterns = [/([\t]*)([\/]{2}|[\/][*]).?import[(]?.?[\"']replace[\"'].?[)]?([*][\/])?/g];
return this.processType("style", findPatterns, replacePatterns);
};
+ Anvil.prototype.initialize = function(config) {
+ this.config = config;
+ this.filesBuilt = {};
+ return this.steps = {
+ source: false,
+ style: false,
+ markup: false,
+ hasSource: config.source,
+ hasStyle: config.style,
+ hasMarkup: config.markup,
+ markupReady: function() {
+ return (this.source || !this.hasSource) && (this.style || !this.hasStyle);
+ },
+ allDone: function() {
+ var status;
+ status = (this.source || !this.hasSource) && (this.style || !this.hasStyle) && (this.markup || !this.hasMarkup);
+ return status;
+ }
+ };
+ };
Anvil.prototype.processType = function(type, findPatterns, replacePatterns) {
var combiner, compiler, forAll, postProcessor, self;
self = this;
@@ -1381,25 +1388,31 @@ Anvil = (function() {
compiler = this.compiler;
combiner = new this.combiner(this.fp, this.scheduler, findPatterns, replacePatterns);
postProcessor = this.postProcessor;
+ this.log.onStep("Starting " + type + " pipe-line");
return self.prepFiles(type, function(list) {
- return self.copyFiles(list, function() {
- return combiner.combineList(list, function() {
- var final;
- final = _.filter(list, function(x) {
- return x.dependents === 0;
- });
- if (self.config.docs) {
- self.documenter.generate(final);
- }
- return forAll(final, compiler.compile, function(compiled) {
- return postProcessor[type].process(compiled, function(list) {
- return self.finalOutput(list, function() {
- return self.stepComplete(type);
+ if (list && list.length > 0) {
+ return self.copyFiles(list, function() {
+ self.log.onStep("Combining " + type + " files");
+ return combiner.combineList(list, function() {
+ var final;
+ final = _.filter(list, function(x) {
+ return x.dependents === 0;
+ });
+ self.log.onStep("Compiling " + type + " files");
+ return forAll(final, compiler.compile, function(compiled) {
+ self.log.onStep("Post-process " + type + " files");
+ return postProcessor[type].process(compiled, function(list) {
+ self.log.onStep("Moving " + type + " files to destinations");
+ return self.finalOutput(list, function() {
+ return self.stepComplete(type);
+ });
});
});
});
});
- });
+ } else {
+ return self.stepComplete(type);
+ }
});
};
Anvil.prototype.finalOutput = function(files, onComplete) {
@@ -1437,7 +1450,9 @@ Anvil = (function() {
fp = this.fp;
forAll = this.scheduler.parallel;
return fp.getFiles(this.config.working, function(files) {
- return forAll(files, fp["delete"], onComplete);
+ return forAll(files, fp["delete"], function() {
+ return onComplete();
+ });
});
};
Anvil.prototype.prepFiles = function(type, onComplete) {
@@ -1450,7 +1465,7 @@ Anvil = (function() {
log = this.log;
return this.fp.getFiles(typePath, function(files) {
var file, filtered, list, name;
- log.onEvent("Scanning " + files.length + " " + type + " files ...");
+ log.onEvent("Found " + files.length + " " + type + " files ...");
list = (function() {
var _i, _len, _results;
_results = [];
@@ -1515,8 +1530,9 @@ Continuous = (function() {
}
};
Continuous.prototype.setup = function() {
- var p, _i, _j, _k, _l, _len, _len2, _len3, _len4, _ref, _ref2, _ref3, _ref4;
+ var p, _i, _j, _k, _l, _len, _len2, _len3, _len4, _ref, _ref2, _ref3, _ref4, _results;
if (!this.watching) {
+ this.watching = true;
if (this.style) {
_ref = this.style;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -1540,13 +1556,14 @@ Continuous = (function() {
}
if (this.spec) {
_ref4 = this.spec;
+ _results = [];
for (_l = 0, _len4 = _ref4.length; _l < _len4; _l++) {
p = _ref4[_l];
- this.watchPath(p);
+ _results.push(this.watchPath(p));
}
+ return _results;
}
}
- return this.watching = true;
};
Continuous.prototype.watchPath = function(path) {
return this.fp.getFiles(path, this.watchFiles);
@@ -1561,20 +1578,24 @@ Continuous = (function() {
return _results;
};
Continuous.prototype.onEvent = function(event, file) {
- this.watching = false;
- while (this.watchers.length > 0) {
- this.watchers.pop().close();
+ if (this.watching) {
+ this.watching = false;
+ while (this.watchers.length > 0) {
+ this.watchers.pop().close();
+ }
+ return this.onChange();
}
- return this.onChange();
};
return Continuous;
})();
mocha = require("mocha");
+_ = require("underscore");
reporters = mocha.reporters;
interfaces = mocha.interfaces;
Context = mocha.Context;
Runner = mocha.Runner;
Suite = mocha.Suite;
+path = require("path");
/*
This class is an adaptation of the code found in _mocha
from TJ Holowaychuk's Mocha repository:
@@ -1586,6 +1607,7 @@ MochaRunner = (function() {
this.scheduler = scheduler;
this.config = config;
this.onComplete = onComplete;
+ _.bindAll(this);
}
MochaRunner.prototype.run = function() {
var Base, Reporter, filesIn, forAll, opts, reporterName, self, specs, suite, ui, uiName, _base;
@@ -1620,6 +1642,7 @@ MochaRunner = (function() {
specs = _.isString(this.config.spec) ? [this.config.spec] : this.config.spec;
return forAll(specs, this.fp.getFiles, function(lists) {
var file, files, reporter, runner, _i, _len;
+ self.cleanUp();
files = _.flatten(lists);
for (_i = 0, _len = files.length; _i < _len; _i++) {
file = files[_i];
@@ -1640,6 +1663,19 @@ MochaRunner = (function() {
});
}
};
+ MochaRunner.prototype.cleanUp = function() {
+ var cachedFiles, file, modulePath, pathLength, sourcePath, _i, _len, _results;
+ cachedFiles = _.flatten(require.cache);
+ sourcePath = path.resolve(this.config.source);
+ pathLength = sourcePath.length;
+ _results = [];
+ for (_i = 0, _len = cachedFiles.length; _i < _len; _i++) {
+ file = cachedFiles[_i];
+ modulePath = file.filename.substring(0, pathLength);
+ _results.push(sourcePath === modulePath ? delete require.cache[file] : void 0);
+ }
+ return _results;
+ };
return MochaRunner;
})();
SocketServer = (function() {
@@ -1751,65 +1787,94 @@ Host = (function() {
};
return Host;
})();
-exports.run = function() {
- var anvil, ci, compiler, configuration, crawler, documenter, fileChange, fp, mochaRunner, parser, scheduler, socketServer;
- scheduler = new Scheduler();
- parser = new ArgParser();
- crawler = new FSCrawler(scheduler);
- fp = new FSProvider(crawler, log);
- configuration = new Configuration(fp, parser, scheduler, log);
- compiler = new Compiler(fp, log);
- mochaRunner = void 0;
- ci = void 0;
- documenter = void 0;
- anvil = {};
- socketServer = {};
- fileChange = function() {};
- return configuration.configure(function(config, stop) {
- var postProcessor, server;
+Cli = (function() {
+ function Cli() {
+ this.anvil = {};
+ this.ci = void 0;
+ this.documenter = void 0;
+ this.mochaRunner = void 0;
+ this.socketServer = {};
+ this.postProcessor = {};
+ this.log = log;
+ this.scheduler = new Scheduler();
+ this.crawler = new FSCrawler(this.scheduler);
+ this.fp = new FSProvider(this.crawler, this.log);
+ this.configuration = new Configuration(this.fp, this.scheduler, this.log);
+ this.compiler = new Compiler(this.fp, this.log);
+ _.bindAll(this);
+ }
+ Cli.prototype.initCI = function(config) {
+ return this.ci = new Continuous(this.fp, config, this.onFileChange);
+ };
+ Cli.prototype.initHost = function(config) {
+ this.server = new Host(this.fp, this.scheduler, this.compiler, config);
+ this.socketServer = new SocketServer(this.server.app);
+ return this.log.onStep("Static HTTP server listening on port " + config.port);
+ };
+ Cli.prototype.initMocha = function(config) {
+ return this.mochaRunner = new MochaRunner(this.fp, this.scheduler, config, this.onTestsComplete);
+ };
+ Cli.prototype.notifyHttpClients = function() {
+ if (this.socketServer.refreshClients) {
+ this.log.onStep("Notifying clients of build completion");
+ return this.socketServer.refreshClients();
+ }
+ };
+ Cli.prototype.onBuildComplete = function() {
+ var self;
+ self = this;
+ this.log.onComplete("Build " + (this.anvil.buildNumber++) + " completed");
+ if (self.mochaRunner) {
+ self.log.onStep("Running specifications with Mocha");
+ return self.mochaRunner.run();
+ } else {
+ self.startCI();
+ return self.notifyHttpClients();
+ }
+ };
+ Cli.prototype.onConfig = function(config, stop) {
+ this.config = config;
if (stop) {
process.exit(0);
}
if (config.continuous) {
- ci = new Continuous(fp, config, function() {
- return fileChange();
- });
+ this.initCI(config);
}
if (config.mocha) {
- mochaRunner = new MochaRunner(fp, scheduler, config, function() {
- return log.onComplete("tests complete");
- });
+ this.initMocha(config);
}
if (config.host) {
- server = new Host(fp, scheduler, compiler, config);
- socketServer = new SocketServer(server.app);
+ this.initHost(config);
}
- postProcessor = new PostProcessor(config, fp, scheduler, log);
- documenter = new Documenter(config, fp, scheduler, log);
- anvil = new Anvil(config, fp, compiler, Combiner, documenter, scheduler, postProcessor, log, function() {
- log.onComplete("build done");
- if (mochaRunner) {
- setTimeout(function() {
- return mochaRunner.run();
- }, 200);
- }
- if (ci) {
- setTimeout(function() {
- return ci.setup();
- }, 200);
- }
- if (socketServer.refreshClients) {
- return setTimeout(function() {
- return socketServer.refreshClients();
- }, 200);
- }
- });
- fileChange = function() {
- return anvil.build();
- };
- anvil.build();
- if (ci) {
- return ci.setup();
+ this.postProcessor = new PostProcessor(config, this.fp, this.scheduler, this.log);
+ this.documenter = new Documenter(config, this.fp, this.scheduler, this.log);
+ this.anvil = new Anvil(this.fp, this.compiler, Combiner, this.documenter, this.scheduler, this.postProcessor, this.log, this.onBuildComplete);
+ this.anvil.build(config);
+ return this.startCI();
+ };
+ Cli.prototype.onFileChange = function() {
+ this.log.onEvent("File change detected, starting build");
+ this.fileChange = function() {};
+ return this.anvil.build(this.config);
+ };
+ Cli.prototype.onTestsComplete = function() {
+ this.log.onComplete("Tests completed");
+ this.startCI();
+ return this.notifyHttpClients();
+ };
+ Cli.prototype.run = function() {
+ return this.configuration.configure(process.argv, this.onConfig);
+ };
+ Cli.prototype.startCI = function() {
+ if (this.ci) {
+ this.log.onStep("Starting file watchers");
+ return this.ci.setup();
}
- });
+ };
+ return Cli;
+})();
+exports.run = function() {
+ var cli;
+ cli = new Cli();
+ return cli.run();
};
View
4 package.json
@@ -1,7 +1,7 @@
{
"name": "anvil.js",
"description": "A continuous integration tool for JS, CSS and HTML.",
- "version": "0.7.3",
+ "version": "0.7.4",
"authors": ["Alex Robson <@A_Robson>"],
"keywords": ["build","compile","static","ci"],
"main": "./lib/anvil.js",
@@ -20,7 +20,7 @@
"underscore": ">=1.1.7",
"coffee-script": "1.1.2",
"express": ">=2.4.6",
- "argparser": "0.0.9",
+ "commander": ">=0.6.0",
"socket.io": ">=0.8.4",
"stylus": ">=0.24.0",
"cssmin": ">=0.3.1",
View
1  spec/anvil.specs.coffee
@@ -1,7 +1,6 @@
_ = require "underscore"
log = require( "./logMock.coffee" ).log
FP = require( "./fsMock.coffee" ).fsProvider
-ArgParser = require( "./argParserMock.coffee" ).parser
Anvil = require( "../src/anvil")
Scheduler = require( "../src/scheduler.coffee").scheduler
scheduler = new Scheduler()
View
18 spec/argParserMock.coffee
@@ -1,18 +0,0 @@
-_ = require "underscore"
-
-class ArgParserMock
-
- constructor: ( @options ) ->
-
- addValueOptions: ( list ) ->
- @optionsAdded = list
-
- parse: ->
-
- getOptions: () ->
- self = this
- list = Array.prototype.slice.call arguments
- values = ( self.options[ key ] for key in list )
- _.find values, ( x ) -> x
-
-exports.parser = ArgParserMock
View
87 spec/config.specs.coffee
@@ -1,7 +1,6 @@
_ = require "underscore"
log = require( "./logMock.coffee" ).log
FP = require( "./fsMock.coffee" ).fsProvider
-ArgParser = require( "./argParserMock.coffee" ).parser
Configuration = require( "../src/config").configuration
Scheduler = require( "../src/scheduler.coffee").scheduler
scheduler = new Scheduler()
@@ -46,11 +45,10 @@ class Anvil
describe "when building in lib without build file", ->
fp = new FP()
- parser = new ArgParser( {} )
- cp = new Configuration fp, parser, scheduler, log
+ cp = new Configuration fp, scheduler, log
it "should provide default lib configuration", ( done ) ->
- cp.configure ( config ) ->
+ cp.configure [ "coffee", "./bin/anvil" ], ( config ) ->
defaultLibConfig.output =
"style": "lib"
"source": "lib"
@@ -60,14 +58,13 @@ describe "when building in lib without build file", ->
describe "when building in site without build file", ->
fp = new FP()
- parser = new ArgParser( {} )
- cp = new Configuration fp, parser, scheduler, log
+ cp = new Configuration fp, scheduler, log
before ( done ) ->
fp.ensurePath "./site", done
it "should provide default site configuration", ( done ) ->
- cp.configure ( config ) ->
+ cp.configure [ "coffee", "./bin/anvil" ], ( config ) ->
_.isEqual( config, defaultSiteConfig ).should.be.ok
done()
@@ -94,53 +91,39 @@ describe "when using default build.json file", ->
json = JSON.stringify build
fp.write "./build.json", json, done
- parser = new ArgParser( {} )
- cp = new Configuration fp, parser, scheduler, log
+ cp = new Configuration fp, scheduler, log
- it "should use the loaded file", ( complete ) ->
- cp.configure ( config ) ->
+ it "should use the loaded file", ( done ) ->
+ cp.configure [ "coffee", "./bin/anvil" ], ( config ) ->
build.working = "./tmp"
_.isEqual( config, build ).should.be.ok
- complete()
-
-describe "when requesting version", ->
- fp = new FP()
- parser = new ArgParser { "v": true }
- cp = new Configuration fp, parser, scheduler, log
-
- it "should write version to console", ( done ) ->
- cp.configure ( config ) ->
- _.find( log.messages, ( m ) -> m.match ///Anvil.js.*[0-9].[0-9].[0-9]/// ).should.be.ok
done()
describe "when specifying CI", ->
fp = new FP()
- parser = new ArgParser { "ci": true }
- cp = new Configuration fp, parser, scheduler, log
+ cp = new Configuration fp, scheduler, log
it "should set continuous flag", ( done ) ->
- cp.configure ( config ) ->
+ cp.configure [ "coffee", "./bin/anvil", "--ci" ], ( config ) ->
config.continuous.should.be.ok
done()
describe "when specifying hosting", ->
fp = new FP()
- parser = new ArgParser { "host": true }
- cp = new Configuration fp, parser, scheduler, log
+ cp = new Configuration fp, scheduler, log
it "should set host flag", ( done ) ->
- cp.configure ( config ) ->
+ cp.configure [ "coffee", "./bin/anvil", "--host" ], ( config ) ->
config.host.should.be.ok
done()
describe "when lib scaffold is requested", ->
fp = new FP()
- parser = new ArgParser { "lib": "newlib" }
- cp = new Configuration fp, parser, scheduler, log
+ cp = new Configuration fp, scheduler, log
config = {}
before ( done ) ->
- cp.configure ( cfg ) ->
+ cp.configure [ "coffee", "./bin/anvil", "--lib", "newlib" ], ( cfg ) ->
config = cfg
done()
@@ -154,16 +137,14 @@ describe "when lib scaffold is requested", ->
delete config[ "host" ]
delete config[ "continuous" ]
_.isEqual( config, defaultLibConfig ).should.be.ok
- #( JSON.stringify config ).should.equal( JSON.stringify defaultLibConfig )
describe "when site scaffold is requested", ->
fp = new FP()
- parser = new ArgParser { "site": "newSite" }
- cp = new Configuration fp, parser, scheduler, log
+ cp = new Configuration fp, scheduler, log
config = {}
before ( done ) ->
- cp.configure ( cfg ) ->
+ cp.configure [ "coffee", "./bin/anvil", "--site", "newSite" ], ( cfg ) ->
config = cfg
done()
@@ -183,11 +164,10 @@ describe "when site scaffold is requested", ->
describe "when requesting new lib build file", ->
fp = new FP()
- parser = new ArgParser( { "libfile": "new" } )
- cp = new Configuration fp, parser, scheduler, log
+ cp = new Configuration fp, scheduler, log
it "should create the default lib configuration", ( done ) ->
- cp.configure ( config ) ->
+ cp.configure [ "coffee", "./bin/anvil", "--libfile", "new" ], ( config ) ->
fp.read "new.json", ( content ) ->
obj = JSON.parse content
delete obj["host"]
@@ -198,16 +178,16 @@ describe "when requesting new lib build file", ->
describe "when requesting new site build file", ->
fp = new FP()
- parser = new ArgParser( { "sitefile": "new" } )
- cp = new Configuration fp, parser, scheduler, log
+ process.argv.push "--sitefile"
+ process.argv.push "new"
+ cp = new Configuration fp, scheduler, log
it "should create the default site configuration", ( done ) ->
- cp.configure ( config ) ->
+ cp.configure [ "coffee", "./bin/anvil", "--sitefile", "new" ], ( config ) ->
fp.read "new.json", ( content ) ->
obj = JSON.parse content
delete obj["host"]
delete obj["continuous"]
-
_.isEqual( obj, defaultSiteConfig ).should.be.ok
done()
@@ -253,11 +233,10 @@ describe "when finalize has string header only", ->
json = JSON.stringify build
fp.write "./build.json", json, done
- parser = new ArgParser( {} )
- cp = new Configuration fp, parser, scheduler, log
+ cp = new Configuration fp, scheduler, log
it "should use the loaded file", ( complete ) ->
- cp.configure ( config ) ->
+ cp.configure [ "coffee", "./bin/anvil" ], ( config ) ->
build.working = "./tmp"
_.isEqual( config, expected ).should.be.ok
complete()
@@ -305,11 +284,10 @@ describe "when finalize has a file header only", ->
fp.write "./build.json", json, () ->
fp.write "test.txt", "// this is a test header", done
- parser = new ArgParser( {} )
- cp = new Configuration fp, parser, scheduler, log
+ cp = new Configuration fp, scheduler, log
it "should use the loaded file", ( complete ) ->
- cp.configure ( config ) ->
+ cp.configure [ "coffee", "./bin/anvil" ], ( config ) ->
build.working = "./tmp"
_.isEqual( config, expected ).should.be.ok
complete()
@@ -354,11 +332,10 @@ describe "when wrapping with strings", ->
json = JSON.stringify build
fp.write "./build.json", json, done
- parser = new ArgParser( {} )
- cp = new Configuration fp, parser, scheduler, log
+ cp = new Configuration fp, scheduler, log
it "should normalize the wrapper", ( complete ) ->
- cp.configure ( config ) ->
+ cp.configure [ "coffee", "./bin/anvil" ], ( config ) ->
build.working = "./tmp"
_.isEqual( config, expected ).should.be.ok
complete()
@@ -381,11 +358,10 @@ describe "when using a single name customization", ->
json = JSON.stringify build
fp.write "./build.json", json, done
- parser = new ArgParser( {} )
- cp = new Configuration fp, parser, scheduler, log
+ cp = new Configuration fp, scheduler, log
it "should create any path as part of the name", ( complete ) ->
- cp.configure ( config ) ->
+ cp.configure [ "coffee", "./bin/anvil" ], ( config ) ->
exists = fp.pathExists "lib/test/this/is/so/fun"
exists.should.be.ok
complete()
@@ -411,11 +387,10 @@ describe "when using a multiple name customizations", ->
json = JSON.stringify build
fp.write "./build.json", json, done
- parser = new ArgParser( {} )
- cp = new Configuration fp, parser, scheduler, log
+ cp = new Configuration fp, scheduler, log
it "should create all paths as part of the name", ( complete ) ->
- cp.configure ( config ) ->
+ cp.configure [ "coffee", "./bin/anvil" ], ( config ) ->
fp.pathExists( "lib/test/this/is/so/fun" ).should.be.ok
fp.pathExists( "lib/this/is/also/pretty/great" ).should.be.ok
complete()
View
148 src/cli.coffee
@@ -1,76 +1,94 @@
-# ## run ##
-# This is the function that gets exported for the CLI script.
-# It gets the configuration and determines how to invoke Anvil
-# and supporting behaviors ( CI, Host, Test runner ) based on
-# the configuration.
-# Most of the 'wierdness' in this file revolves around late binding
-# due to most things needing a hook to the completed config before they
-# can properly instantiate. If it hurts your eyes, look away :)
-exports.run = ->
- scheduler = new Scheduler()
- parser = new ArgParser()
- crawler = new FSCrawler( scheduler )
- fp = new FSProvider( crawler, log )
- configuration = new Configuration fp, parser, scheduler, log
- compiler = new Compiler fp, log
- mochaRunner = undefined
- ci = undefined
- documenter = undefined
- anvil = {}
- socketServer = {}
- fileChange = ->
- configuration.configure ( config, stop ) ->
- if stop
- process.exit 0
+# # Cli
+# Provides the command line interface for interacting with Anvil and related modules
+class Cli
+
+ constructor: () ->
+ @anvil = {}
- # if the user wants CI, setup the continuous module
- if config.continuous
- ci = new Continuous fp, config, () ->
- fileChange()
+ @ci = undefined
+ @documenter = undefined
+ @mochaRunner = undefined
+ @socketServer = {}
+ @postProcessor = {}
+ @log = log
+ @scheduler = new Scheduler()
+ @crawler = new FSCrawler @scheduler
+ @fp = new FSProvider @crawler, @log
+ @configuration = new Configuration @fp, @scheduler, @log
+ @compiler = new Compiler @fp, @log
+
+ _.bindAll this
+
+ initCI: ( config ) ->
+ @ci = new Continuous @fp, config, @onFileChange
+
+ initHost: ( config ) ->
+ @server = new Host @fp, @scheduler, @compiler, config
+ @socketServer = new SocketServer @server.app
+ @log.onStep "Static HTTP server listening on port #{ config.port }"
+
+ initMocha: ( config ) ->
+ @mochaRunner = new MochaRunner @fp, @scheduler, config, @onTestsComplete
+
+ notifyHttpClients: () ->
+ if @socketServer.refreshClients
+ @log.onStep "Notifying clients of build completion"
+ @socketServer.refreshClients()
+ onBuildComplete: () ->
+ self = this
+ @log.onComplete "Build #{ @anvil.buildNumber++ } completed"
+ if self.mochaRunner
+ # wrap the mocha runner invocation in a timeout call
+ # to prevent odd timing issues.
+ self.log.onStep "Running specifications with Mocha"
+ self.mochaRunner.run()
+ else
+ self.startCI()
+ self.notifyHttpClients()
+
+ onConfig: ( config, stop ) ->
+ @config = config
+ # if stop comes back, then this is not a build and we're done
+ if stop then process.exit 0
+
+ # if the user wants CI, setup the continuous module
+ if config.continuous then @initCI config
+
# if the user wants mocha to run after the build, setup the mocha runner
- if config.mocha
- mochaRunner = new MochaRunner fp, scheduler, config, () ->
- log.onComplete "tests complete"
+ if config.mocha then @initMocha config
# if the user wants hosting then, spin up the Static HTTP host and socket server
- if config.host
- server = new Host fp, scheduler, compiler, config
- socketServer = new SocketServer server.app
+ if config.host then @initHost config
# create the post processor instance
- postProcessor = new PostProcessor config, fp, scheduler, log
- documenter = new Documenter config, fp, scheduler, log
- anvil = new Anvil config, fp, compiler, Combiner, documenter, scheduler, postProcessor, log, () ->
- log.onComplete "build done"
- if mochaRunner
- # wrap the mocha runner invocation in a timeout call
- # to prevent odd timing issues.
- setTimeout(
- () -> mochaRunner.run(),
- 200
- )
+ @postProcessor = new PostProcessor config, @fp, @scheduler, @log
+ @documenter = new Documenter config, @fp, @scheduler, @log
+ @anvil = new Anvil @fp, @compiler, Combiner, @documenter, @scheduler, @postProcessor, @log, @onBuildComplete
+
+ @anvil.build( config )
+ # if we're using CI, kick it off the first time
+ @startCI()
- if ci
- # wrap the CI watcher in a timeout call
- # to prevent odd timing issues.
- setTimeout(
- () -> ci.setup(),
- 200
- )
+ onFileChange: () ->
+ @log.onEvent "File change detected, starting build"
+ @fileChange = ->
+ @anvil.build( @config )
- if socketServer.refreshClients
- # don't notify the clients immediate after the build
- setTimeout(
- () -> socketServer.refreshClients(),
- 200
- )
+ onTestsComplete: () ->
+ @log.onComplete "Tests completed"
+ @startCI()
+ @notifyHttpClients()
+
+ run: () ->
+ @configuration.configure process.argv, @onConfig
- fileChange = -> anvil.build()
-
- anvil.build()
- # if we're using CI, kick it off the first time
- if ci
- ci.setup()
+ startCI: () ->
+ if @ci
+ @log.onStep "Starting file watchers"
+ @ci.setup()
+
-
+exports.run = ->
+ cli = new Cli()
+ cli.run()
View
2  src/compile.coffee
@@ -47,6 +47,7 @@ class Compiler
newExt = @extensionMap[ ext ]
newFile = file.name.replace ext, newExt
log = @log
+ log.onEvent "Compiling #{ file.name } to #{ newFile }"
compiler = @compilers[ ext ]
if compiler
@fp.transform(
@@ -58,6 +59,7 @@ class Compiler
file.name = newFile
onComplete file
else
+ log.onError "Error compiling #{ file.name }: \r\n #{ err }"
onComplete err
)
else
View
105 src/config.coffee
@@ -1,5 +1,6 @@
_ = require "underscore"
path = require "path"
+Commander = require( "Commander" ).Command
# Configuration container
config = { }
@@ -47,7 +48,6 @@ defaultDoc =
output: "docs"
continuous = test = inProcess = quiet = debug = false
-version = "0.7.3"
ext =
gzip: "gz"
@@ -71,97 +71,70 @@ extensionLookup =
# Calling anvil from the command line runs this.
class Configuration
- constructor: ( @fp, @parser, @scheduler, @log ) ->
+ constructor: ( @fp, @scheduler, @log ) ->
# ## configure ##
# this call will return a configuration object that will
# inform the rest of the process
# * _onConfig {Function}_: the callback to invoke with a configuration object
- configure: ( onConfig ) ->
+ configure: ( argList, onConfig ) ->
self = this
-
- # Setup the CLI arg parser and parse all the args
- @parser.addValueOptions [ "b", "build", "n", "html", "site", "lib", "libfile", "sitefile" ]
- @parser.parse()
-
- # Get build file from CLI args or use default
- buildOpt = @parser.getOptions "b", "build"
- buildFile = if buildOpt then buildOpt else "./build.json"
-
- # create a new lib build file?
- createLibFile = @parser.getOptions "libfile"
-
- # create a new site build file?
- createSiteFile = @parser.getOptions "sitefile"
-
- # Run as CI server?
- continuous = @parser.getOptions "ci"
-
- # host site ?
- host = @parser.getOptions "h", "host"
-
- # Run specs via Mocha?
- useMocha = @parser.getOptions "mocha"
-
- # Generate scaffold for new lib project?
- libScaffold = @parser.getOptions "lib"
-
- #Quiet mode
- quiet = @parser.getOptions "q", "quiet"
-
- # Show version info?
- showVersion = @parser.getOptions "v", "version"
-
- # Generate scaffold for new site project?
- siteScaffold = @parser.getOptions "site"
-
- # Generate annotated source with docco?
- runDocco = @parser.getOptions "docco"
-
- # Generate annotated source with ape?
- runApe = @parser.getOptions "ape"
-
- if showVersion
- # Display version info and exit
- @log.onEvent "Anvil.js " + version
- onConfig config, true
- else if createLibFile or createSiteFile
+ command = new Commander()
+ command
+ .version("0.7.4")
+ .option( "-b, --build [build file]", "Use a custom build file", "./build.json" )
+ .option( "--ci", "Run a continuous integration build" )
+ .option( "--host", "Setup a static HTTP host" )
+ .option( "--lib [project]", "Create a lib project at the folder [project]" )
+ .option( "--libfile [file name]", "Create a new lib build file named [file name]" )
+ .option( "--site [project]", "Create a site project at the folder [project]" )
+ .option( "--sitefile [file name]", "Create a new site build file named [file name]" )
+ .option( "--mocha", "Run specifications using Mocha" )
+ .option( "--docco", "Create annotated source using docco" )
+ .option( "--ape", "Create annotated source using ape" )
+ .option( "-q, --quiet", "Only print completion and error messages" )
+
+ command.parse( argList );
+
+ if command.libfile or command.sitefile
# Generate all the directories and the config file
- name = createLibFile or= createSiteFile
- type = if createSiteFile then 'site' else 'lib'
+ name = command.libfile or= command.sitefile
+ type = if command.sitefile then 'site' else 'lib'
@writeConfig type, "#{name}.json", () ->
+ self.log.onComplete "Created #{ type } build file - #{ name }"
onConfig config, true
- else if siteScaffold or libScaffold
+ else if command.site or command.lib
# Generate all the directories and the config file
- type = if siteScaffold then 'site' else 'lib'
- scaffold = siteScaffold or= libScaffold
+ type = if command.site then 'site' else 'lib'
+ scaffold = command.site or= command.lib
config = if type == 'site' then siteConfig else libConfig
@log.onStep "Creating scaffolding for new #{ type } project"
# Create all the directories
self.ensurePaths( () ->
self.writeConfig( type, scaffold + "/build.json", () ->
- self.log.onComplete "Scaffold #{ scaffold } created!"
+ self.log.onComplete "Scaffold ( #{ scaffold } ) created."
onConfig config, true
)
, scaffold )
else
- @log.onStep "Checking for config..."
+ buildFile = command.build
+ @log.onStep "Checking for #{ buildFile }"
exists = @fp.pathExists buildFile
@prepConfig exists, buildFile, () ->
- if host
+ if command.host
config.host = true
- if continuous
+ if command.ci
config.continuous = true
- if useMocha
+ if command.mocha
config.mocha = defaultMocha
- if runApe
+ if command.ape
config.docs = defaultDoc
config.docs.generator = "ape"
- if runDocco
+ if command.docco
config.docs = defaultDoc
# Run transforms and generate output
@@ -188,7 +161,6 @@ class Configuration
global.process.exit(0)
config
-
# ## ensurePaths ##
# Make sure that all expected paths exist
# ### Args:
@@ -240,6 +212,7 @@ class Configuration
catch err
done()
+ @log.onStep "Ensuring project directory structure"
@scheduler.parallel paths, worker, () -> self.copyPrereqs onComplete
# ## prepConfig ##
@@ -256,7 +229,6 @@ class Configuration
else
@loadConfig( file, onDone )
-
# ## loadConfig ##
# Setup full configuration using specified config file
# For example, anvil -b custom.json
@@ -280,8 +252,9 @@ class Configuration
# ### Args:
# * _onComplete {Function}_: what to do after config is setup
loadConvention: ( onComplete ) ->
- conventionConfig = if @fp.pathExists "./site" then siteConfig else libConfig
- @log.onStep "Loading convention..."
+ isSite = @fp.pathExists "./site"
+ conventionConfig = if isSite then siteConfig else libConfig
+ @log.onStep "No build file found, using #{ if isSite then 'site' else 'lib' } conventions"
config = conventionConfig
onComplete()
View
12 src/continuous.coffee
@@ -23,13 +23,12 @@ class Continuous
# if any contents change
setup: () ->
if not @watching
+ @watching = true
if @style then @watchPath p for p in @style
if @source then @watchPath p for p in @source
if @markup then @watchPath p for p in @markup
if @spec then @watchPath p for p in @spec
- @watching = true
-
# ## watchpath ##
# Calls watchFiles for all files in the path
# * _path {String/Array}_: the path specification to watch for changes in
@@ -50,7 +49,8 @@ class Continuous
# * _event {Object}_: the event that fired on the file system
# * _file {String}_: the file that triggered the change
onEvent: ( event, file ) ->
- @watching = false
- while @watchers.length > 0
- @watchers.pop().close()
- @onChange()
+ if @watching
+ @watching = false
+ while @watchers.length > 0
+ @watchers.pop().close()
+ @onChange()
View
8 src/documenter.coffee
@@ -30,8 +30,10 @@ class Documenter
# * _files {Array}_: the array of file objects to create documents for
generate: ( files ) ->
self = this
- @scheduler.parallel files, @document, () ->
- self.log.onComplete "Documents have been completed"
+ if files && files.length > 0
+ @log.onEvent "Creating annotated source for: #{ _.pluck( files, 'name' ).toString() }"
+ @scheduler.parallel files, @document, () ->
+ self.log.onComplete "Code annotation completed"
# ## document ##
# Generate docco/ape annotated source for the combined file
@@ -44,7 +46,7 @@ class Documenter
ext = file.ext()
newFile = file.name.replace ext, ".html"
- @log.onStep "Generating source documentation for #{ file.name }"
+ @log.onEvent "Annotation for #{ file.name }"
@fp.read [ file.workingPath, file.name ], ( content ) ->
self.generator language, ext, newFile, content, ( doc ) ->
self.fp.write [ self.config.docs.output, newFile ], doc, onComplete
View
4 src/libs.coffee
@@ -20,10 +20,6 @@ mkdir = require( "mkdirp" ).mkdirp
# Node's path helper library
path = require "path"
-# Parses command line args and options --
-# See https://github.com/shinout/argparser for more info
-ArgParser = require "argparser"
-
# A Sinatra inspired web development framework for Node --
# See http://expressjs.com for more info
express = require "express"
View
87 src/main.coffee
@@ -2,30 +2,18 @@
# This provides the primary logic and flow control for build activities
class Anvil
- constructor: ( @config, @fp, @compiler, @combiner, @documenter, @scheduler, @postProcessor, @log, @callback ) ->
- config = @config
- @filesBuilt = {}
+ constructor: ( @fp, @compiler, @combiner, @documenter, @scheduler, @postProcessor, @log, @callback ) ->
+ @buildNumber = 0
@inProcess = false
- # mini FSM - basically we don't want to start building markup until
- # everything else is done since markup can import other built resources
- @steps =
- source: false
- style: false
- markup: false
- hasSource: config.source
- hasStyle: config.style
- hasMarkup: config.markup
- markupReady: () -> ( this.source or not this.hasSource ) and ( this.style or not this.hasStyle )
- allDone: () ->
- status = ( this.source or not this.hasSource ) and ( this.style or not this.hasStyle ) and ( this.markup or not this.hasMarkup )
- status
-
+
extensions: [ ".js", ".coffee", ".html", ".haml", ".markdown", ".md", ".css", ".styl", ".less", ".css" ]
# ## build ##
# Kicks off the build for the currently configured Anvil instance
- build: () ->
+ build: ( config ) ->
if not @inProcess
+ @initialize( config )
+ @log.onStep "Build #{ @buildNumber } initiated"
@inProcess = true
@buildSource()
@buildStyle()
@@ -54,6 +42,25 @@ class Anvil
replacePatterns = [ ///([ \t]*)([/]{2}|[/][*]).?import[(]?.?[\"']replace[\"'].?[)]?([*][/])?///g ]
@processType( "style", findPatterns, replacePatterns )
+ # ## initialize
+ # Initializes state for the build
+ initialize: ( config ) ->
+ @config = config
+ @filesBuilt = {}
+ # mini FSM - basically we don't want to start building markup until
+ # everything else is done since markup can import other built resources
+ @steps =
+ source: false
+ style: false
+ markup: false
+ hasSource: config.source
+ hasStyle: config.style
+ hasMarkup: config.markup
+ markupReady: () -> ( this.source or not this.hasSource ) and ( this.style or not this.hasStyle )
+ allDone: () ->
+ status = ( this.source or not this.hasSource ) and ( this.style or not this.hasStyle ) and ( this.markup or not this.hasMarkup )
+ status
+
# ## processType ##
# The steps that get followed for each resource type are the same.
# This function provides the core behavior of identifying, combining,
@@ -68,22 +75,31 @@ class Anvil
combiner = new @combiner( @fp, @scheduler, findPatterns, replacePatterns )
postProcessor = @postProcessor
+ @log.onStep "Starting #{ type } pipe-line"
self.prepFiles type, ( list ) ->
- self.copyFiles list, () ->
- # combines imported files
- combiner.combineList list, () ->
- # filter out all files that were combined into another file
- final = _.filter( list, ( x ) -> x.dependents == 0 )
- # if documentation should be generated, do that now
- if self.config.docs
- self.documenter.generate final
- # compiles the combined results
- forAll final, compiler.compile, ( compiled ) ->
- # kick off post processors for compiled files
- postProcessor[ type ].process compiled, ( list ) ->
- # copy complete files to the destination folders
- self.finalOutput list, () ->
- self.stepComplete type
+ if list and list.length > 0
+
+ self.copyFiles list, () ->
+ # combines imported files
+ self.log.onStep "Combining #{ type } files"
+ combiner.combineList list, () ->
+ # filter out all files that were combined into another file
+ final = _.filter( list, ( x ) -> x.dependents == 0 )
+ # if documentation should be generated, do that now
+ #if self.config.docs
+ # self.documenter.generate final
+ # compiles the combined results
+ self.log.onStep "Compiling #{ type } files"
+ forAll final, compiler.compile, ( compiled ) ->
+ # kick off post processors for compiled files
+ self.log.onStep "Post-process #{ type } files"
+ postProcessor[ type ].process compiled, ( list ) ->
+ # copy complete files to the destination folders
+ self.log.onStep "Moving #{ type } files to destinations"
+ self.finalOutput list, () ->
+ self.stepComplete type
+ else
+ self.stepComplete type
# ## finalOutput ##
# Copies the final list of files to their output folders
@@ -124,7 +140,8 @@ class Anvil
fp = @fp
forAll = @scheduler.parallel
fp.getFiles @config.working, ( files ) ->
- forAll files, fp.delete, onComplete
+ forAll files, fp.delete, () ->
+ onComplete()
# ## prepFiles ##
@@ -141,7 +158,7 @@ class Anvil
output = if _.isArray( output ) then output else [ output ]
log = @log
@fp.getFiles typePath, ( files ) ->
- log.onEvent "Scanning #{ files.length } #{ type } files ..."
+ log.onEvent "Found #{ files.length } #{ type } files ..."
list = for file in files
name = path.basename file
{
View
21 src/mocha.coffee
@@ -1,9 +1,11 @@
mocha = require "mocha"
+_ = require "underscore"
reporters = mocha.reporters
interfaces = mocha.interfaces
Context = mocha.Context
Runner = mocha.Runner
Suite = mocha.Suite
+path = require "path"
###
This class is an adaptation of the code found in _mocha
@@ -13,6 +15,7 @@ Suite = mocha.Suite
class MochaRunner
constructor: ( @fp, @scheduler, @config, @onComplete ) ->
+ _.bindAll( this )
run: () ->
self = this
@@ -41,9 +44,9 @@ class MochaRunner
specs = if _.isString @config.spec then [ @config.spec ] else @config.spec
forAll specs, @fp.getFiles, ( lists ) ->
+ self.cleanUp()
files = _.flatten lists
for file in files
- delete require.cache[ file ]
suite.emit 'pre-require', global, file
suite.emit 'require', require file, file
suite.emit 'post-require', global, file
@@ -53,4 +56,20 @@ class MochaRunner
reporter = new Reporter runner
if opts.ignoreLeaks then runner.ignoreLeaks = true
runner.run () ->
+ cachedFiles = _.flatten require.cache
+ sourcePath = path.resolve self.config.source
+ pathLength = sourcePath.length
+ for file in cachedFiles
+ modulePath = file.filename.substring 0, pathLength
+ if sourcePath == modulePath
+ delete require.cache[ file ]
self.onComplete()
+
+ cleanUp: () ->
+ cachedFiles = _.flatten require.cache
+ sourcePath = path.resolve @config.source
+ pathLength = sourcePath.length
+ for file in cachedFiles
+ modulePath = file.filename.substring 0, pathLength
+ if sourcePath == modulePath
+ delete require.cache[ file ]
View
15 src/pipeline.coffee
@@ -35,7 +35,10 @@ class StylePipeline
if self.config.cssmin
minified = _.map( files, ( x ) -> _.clone x )
forAll files, self.finalize, () ->
+ self.log.onStep "Finalizing CSS"
forAll minified, self.minify, () ->
+ if minified.length > 0
+ self.log.onStep "Minifying CSS"
forAll minified, self.finalize, () ->
onComplete( files.concat minified )
@@ -45,6 +48,7 @@ class StylePipeline
# * _onComplete {Function}_: the function to call after minification has completed
minify: ( file, onComplete ) ->
if @config.cssmin
+ @log.onEvent "Minifying #{ file.name }"
self = this
ext = file.ext()
newFile = file.name.replace ext, ".min.css"
@@ -69,6 +73,7 @@ class StylePipeline
finalize: ( file, onComplete ) ->
self = this
if @config.finalize and @config.finalize.style
+ @log.onEvent "Finalizing #{ file.name }"
header = @config.finalize.style.header
footer = @config.finalize.style.footer
@fp.transform(
@@ -92,6 +97,7 @@ class StylePipeline
wrap: ( file, onComplete ) ->
self = this
if @config.wrap and @config.wrap.style
+ @log.onEvent "Wrapping #{ file.name }"
prefix = @config.wrap.style.prefix
suffix = @config.wrap.style.suffix
@fp.transform(
@@ -132,7 +138,10 @@ class SourcePipeline
if self.config.uglify
minify = _.map( files, ( x ) -> _.clone x )
forAll files, self.finalize, () ->
+ self.log.onStep "Finalizing source files"
forAll minify, self.minify, () ->
+ if minify.length > 0
+ self.log.onStep "Minifying source files"
forAll minify, self.finalize, () ->
onComplete( files.concat minify )
@@ -145,7 +154,7 @@ class SourcePipeline
self = this
ext = file.ext()
newFile = file.name.replace ext, ".min.js"
- @log.onStep "Uglifying #{ newFile }"
+ @log.onEvent "Minifying #{ newFile }"
@fp.transform(
[ file.workingPath, file.name ],
( content, onTransform ) ->
@@ -171,7 +180,7 @@ class SourcePipeline
finalize: ( file, onComplete ) ->
self = this
if @config.finalize and @config.finalize.source
- @log.onStep "Finalizing #{ file.name }"
+ @log.onEvent "Finalizing #{ file.name }"
header = @config.finalize.source.header
footer = @config.finalize.source.footer
@fp.transform(
@@ -196,7 +205,7 @@ class SourcePipeline
wrap: ( file, onComplete ) ->
self = this
if @config.wrap and @config.wrap.source
- @log.onStep "Wrapping #{ file.name }"
+ @log.onEvent "Wrapping #{ file.name }"
prefix = @config.wrap.source.prefix
suffix = @config.wrap.source.suffix
@fp.transform(

No commit comments for this range

Something went wrong with that request. Please try again.