diff --git a/examples/precompile-sakefile/Sakefile b/examples/precompile-sakefile/Sakefile new file mode 100644 index 0000000..7447199 --- /dev/null +++ b/examples/precompile-sakefile/Sakefile @@ -0,0 +1,10 @@ +// sakefile: { +// function (sakefile) { +// return sakefile.replace("\\" +"default,", '"default",'); +// }; +// sakefile: } + +task(\default, function (t) { + log("Success"); + t.done(); +}); diff --git a/node_modules/sake/sake.js b/node_modules/sake/sake.js index abf0888..8aada9e 100644 --- a/node_modules/sake/sake.js +++ b/node_modules/sake/sake.js @@ -18,6 +18,10 @@ var Proteus = require("proteus"), OPTIONS = {}, NAMESPACES = [], PATH_SPLIT_RE = process.platform === "win32" ? /[\/\\]+/ : /\/+/, + PRECOMPILE_FUN_REGION_START_RE = /^(.*)\ssakefile:\s*{/, + PRECOMPILE_FUN_REGION_END_RE = /\ssakefile:\s*}/, + PRECOMPILE_FUN_REGION_SEARCH_LINES_MAX = 5, + PRECOMPILE_FUN_REGION_LINES_MAX = 100, CURRENT_PATH, sakeContext, futil, @@ -67,6 +71,65 @@ function sakeRequire (name) { } } //--------------------------------------------------------------------------- +// Get source of bootstrap function for precompiling Sakefile. +// +// Function region is bounded by required marker lines which aren't included +// in returned function source. Prefix of start marker line is stripped from +// every function source line. +// +// If function region wasn't found or exceeded limit of lines, empty string +// is returned. +//--------------------------------------------------------------------------- +function extractPrecompileFunctionSource (sakefileSource) { + var codeLineNum = 0, + functionRegionLinesRead = 0, + functionSource = "", + newlinePos = 0, + pos = 0, + codeLine, + marker, + markerLinePrefix; + + while (pos < sakefileSource.length) { + // Find next code line. + newlinePos = sakefileSource.indexOf("\n", pos); + if (newlinePos === -1) { + newlinePos = sakefileSource.length + } + codeLine = sakefileSource.substr(pos, newlinePos - pos); + pos = newlinePos + 1; + + if (!functionRegionLinesRead) { + // Not in function region yet. + // Test read code line for being marker of function region start. + if (marker = PRECOMPILE_FUN_REGION_START_RE.exec(codeLine)) { + functionRegionLinesRead = 1; + markerLinePrefix = marker[1]; + } + else { + // Count read code lines and check for exceeding a limit of + // lines to look in for marker of function region start. + codeLineNum++; + if (codeLineNum >= PRECOMPILE_FUN_REGION_SEARCH_LINES_MAX) { + return ""; + } + } + continue; + } + + if (PRECOMPILE_FUN_REGION_END_RE.test(codeLine)) { + return functionSource; + } + + functionSource += codeLine.replace(markerLinePrefix, "") + "\n"; + + functionRegionLinesRead++; + if (functionRegionLinesRead >= PRECOMPILE_FUN_REGION_LINES_MAX) { + return ""; + } + } +} +//--------------------------------------------------------------------------- // Create the Run Context //--------------------------------------------------------------------------- function getRunContext () { @@ -148,7 +211,7 @@ function createTask (TaskClass, name, prereqs, action) { Proteus.merge(sake, Object.defineProperties({ run: function (filepath) { - var code, ret; + var code, ret, precompileFunSource; log.debug("sake#run"); @@ -164,7 +227,23 @@ Proteus.merge(sake, Object.defineProperties({ log.debug("Reading Sakefile: " + filepath); code = FS.readFileSync(filepath, "utf8"); - if (Path.extname(filepath).toLowerCase() === ".coffee") { + if (Path.extname(filepath) === "") { + precompileFunSource = extractPrecompileFunctionSource(code); + if (precompileFunSource.length > 0) { + try { + code = eval("(" + + precompileFunSource.replace(/;\s*$/, "") + + ")")(code); + } + catch (e) { + log.error("Precompile function failed. " + e + "\n" + + "Precompile function source:\n" + + precompileFunSource); + process.exit(1); + } + } + } + else if (Path.extname(filepath).toLowerCase() === ".coffee") { try { code = require("coffee-script").compile(code); } diff --git a/test/test-precompile-sakefile.js b/test/test-precompile-sakefile.js new file mode 100644 index 0000000..958570d --- /dev/null +++ b/test/test-precompile-sakefile.js @@ -0,0 +1,24 @@ + +var futils = require('sake/file-utils'), + sh = futils.sh; + +function shCallback (done) { + return function (error, result) { + if (error) { + throw new Error(error); + } + done(result); + }; +} +suite('Precompile Sakefile', function () { + + test('works', function (done) { + var cmd = 'sake -f examples/precompile-sakefile/Sakefile'; + + sh(cmd, shCallback(function (result) { + result.should.equal('Success\n'); + done(); + })); + }); + +});