diff --git a/README.md b/README.md index 2ead295..2bb4d9b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ groundskeeper ============= -__Current Version:__ 0.1.12 +__Current Version:__ 0.2.00 [![Build Status](https://secure.travis-ci.org/Couto/groundskeeper.png?branch=master)](https://travis-ci.org/Couto/groundskeeper) [![Dependencies Status](https://david-dm.org/Couto/groundskeeper.png?branch=master)](https://david-dm.org/Couto/groundskeeper) @@ -156,6 +156,10 @@ var clone = function (arr) { Notice those comments? They specify a block code of validation, you can specify whatever name you wish, as long as you respect the format. +Comment blocks can not be nested, and must be open/closed, similar to XML. + +Groundskeeper will throw an error if malformed pragmas are detected so you don't end up with unexpected output. + Tests ----- Tests are ran using [mocha](http://visionmedia.github.com/mocha/) and [jscoverage](https://github.com/visionmedia/node-jscoverage) you can install mocha with `npm install`, but you'll need to clone and install jscoverage from this [repository](https://github.com/visionmedia/node-jscoverage) diff --git a/bin/groundskeeper b/bin/groundskeeper index 91f9e2a..42029c5 100755 --- a/bin/groundskeeper +++ b/bin/groundskeeper @@ -10,7 +10,7 @@ * @author Luis Couto * @organization 15minuteslate.net * @contact couto@15minuteslate.net - * @version 0.1.12 + * @version 0.2.00 * @requires path, fs, exec, commander.js, colors, log.js * @license ISC 2014, Luis Couto * diff --git a/lib/groundskeeper.js b/lib/groundskeeper.js index 3b06342..bf1fe19 100644 --- a/lib/groundskeeper.js +++ b/lib/groundskeeper.js @@ -10,7 +10,7 @@ * @author Luis Couto * @organization 15minuteslate.net * @contact couto@15minuteslate.net - * @version 0.1.12 + * @version 0.2.00 * @requires falafel, stream, util, esprima * @license ISC 2014, Luis Couto */ @@ -242,22 +242,28 @@ Groundskeeper.prototype.removeDebugger = function (node) { */ Groundskeeper.prototype.removePragmas = function (comments, source, pragmas) { - var pragmaMatcher = /^[<][/]*([^\s]*)[>]$/, - // find comments' ranges - ranges = comments + var pragmaMatcher = /^[<]([/])*([^\s]*)[>]$/, + pragmaName = '', // Global tracker to see what pragme we're currently inside. + isPragmaOpen = false, // Global tracker to see if we're inside a pragma. + ranges = comments // Find comments' ranges .map(function (comment) { var matches = pragmaMatcher.exec(comment.value.trim()), - pragmaName = matches && matches[1]; - - // if the comment - // * only contains a `pragma` - // * that pragma is not on the pragmas keep list - if ( - pragmaName && - pragmas.indexOf(pragmaName) === -1 - ) { - return comment.range; + currentPragmaName = matches && matches[2], + isCurrentPragmaOpen = matches && matches[1] === undefined; + + // If we don't have a match, or the pragma is whitelisted, bail out. Else process for legal pragmas. + if(!matches || (currentPragmaName && pragmas.indexOf(currentPragmaName) !== -1)){ + return; + } else if((isCurrentPragmaOpen && !isPragmaOpen) || + (!isCurrentPragmaOpen && currentPragmaName === pragmaName && !isCurrentPragmaOpen)){ + // A legal open is when we're opening a pragma, and we're not inside another open pragma (no nesting) + // A legal close is when we're closing (starts with /), the previous pragma is open, and the names match. + pragmaName = currentPragmaName; + isPragmaOpen = isCurrentPragmaOpen; + return comment.range; + }else{ // If it matched, but it wasn't a legal directive, we have an error and the output will be unexpected. + throw new Error('Groundskeeper: "' + currentPragmaName + '" directive was encountered, but is malformed. Check nearby pragmas.'); } }) @@ -274,11 +280,14 @@ Groundskeeper.prototype.removePragmas = function (comments, source, pragmas) { finalsource = ""; if (ranges && ranges.length) { - while (ranges.length) { + if(ranges.length % 2 !== 0){ // If we have an odd number of pragma start/stops, something is wrong. + throw new Error('Groundskeeper: encountered trailing pragma directive.'); + } + while (ranges.length > 1) { + // >1 because we shift twice. finalsource += source.slice(start, ranges.shift()); start = ranges.shift(); } - finalsource += source.slice(start, source.length); return finalsource; } else { diff --git a/package.json b/package.json index f890dd0..48db85b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "groundskeeper", "description": "Pragmas and console statements remover", - "version": "0.1.12", + "version": "0.2.00", "author": "Luis Couto (15minuteslate.net)", "license": "ISC", "repository": { diff --git a/test/fixtures/pragmas/trailing.js b/test/fixtures/pragmas/trailing.js new file mode 100644 index 0000000..1ed44b0 --- /dev/null +++ b/test/fixtures/pragmas/trailing.js @@ -0,0 +1,11 @@ +function merge(target) { + console.dir(target, arguments); + + var objects = Array.prototype.slice.call(arguments, 1), + keys = [], + log = console.log; + + console.log('keep me!'); + // + console.log('also keep me!'); +} \ No newline at end of file diff --git a/test/fixtures/pragmas/unmatched.js b/test/fixtures/pragmas/unmatched.js new file mode 100644 index 0000000..9498ffd --- /dev/null +++ b/test/fixtures/pragmas/unmatched.js @@ -0,0 +1,22 @@ +function merge(target) { + console.dir(target, arguments); + + var objects = Array.prototype.slice.call(arguments, 1), + keys = [], + log = console.log; + + objects.forEach(function (val, idx) { + keys = Object.keys(val); + keys.forEach(function (val) { + target[val] = objects[idx][val]; + App.logger.warn("Hello World"); + console.log(keys); + debugger; + // + clean('this').developmentPragma; + // + console.log("Sad Panda"); + // + }); + }); +} \ No newline at end of file diff --git a/test/pragma.test.js b/test/pragma.test.js index 7caac8b..f9c89af 100644 --- a/test/pragma.test.js +++ b/test/pragma.test.js @@ -70,5 +70,25 @@ module.exports = { cleaner.write(file); assert.equal(cleaner.toString(), clean); + }, + 'error on unmatched pragmas': function () { + var file = fixture('pragmas/unmatched'), + cleaner = groundskeeper({ + console: true, + 'debugger': true + }); + assert.throws( + function(){ cleaner.write(file); }, + function(err){return err.toString().indexOf('"unmatched" directive was encountered') !== -1;}); + }, + 'error on trailing pragmas': function () { + var file = fixture('pragmas/trailing'), + cleaner = groundskeeper({ + console: true, + 'debugger': true + }); + assert.throws( + function(){ cleaner.write(file); }, + function(err){return err.toString().indexOf('Groundskeeper: encountered trailing pragma directive.') !== -1;}); } };