From 173d2d6c49a3ce40a4377a1b7b82612e4d455a79 Mon Sep 17 00:00:00 2001 From: Ger Hobbelt Date: Mon, 28 Sep 2015 21:04:38 +0200 Subject: [PATCH] Extended on the work done by Scotty Eckenthal (@scottyeck): - extended the test coverage, including some edge cases - `options.replaceContent` now is the `options.replace` setting itself: that option now accepts the search string as it was before by `options.replaceContent`. - augmented the replace abilities of `grunt-banner`: + `options.position`: when set to `replace`, this *implies* `options.replace: true` unless that option has explicitly been set by the user already (see below) + when replacement fails, i.e. no existing banner could be spotted, then `grunt-banner` falls back to regular non-replacement behaviour. In short: `grunt-banner` will always either *replace* or *add* a banner! This fixes the edge condition in the previous work where `position: replace` would *require* a pre-existing banner or else *fail to add* a banner. + `options.replace` parameter now can be: + `false` (default) - do not look for existing banners; simply add the banner at the specified position (top/bottom). + `true` - 'smart' replace mode: use the built-in 'smart' locate-and-mark scanner to dig out the existing banners (more on the rules what maketh a banner below) + (string) - replace any part of the source code which matches this *implicit regex*. This means most strings are matched as-is, but do not get mistaken about this: dot `.`, star `*` et al will not be the *literal characters* you might have expected, but are treated as regex operators! E.g. `replace: "/* blurb */"` will **not** work as a literal string, as the stars `*` in there will make it match lines like `// blurb //` but **will not** match an actual C-style comment line `/* blurb */`; you will need to specify the proper regex for that instead: `replace: "\/\* blurb \*\/"`. + (RegExp) - a rexexp instance to match against. Otherwise the same as for the (string) type value above. + (function) - provide your own callback method to locate and mark the input. The interface for the callback function is: ``` function (fileContents, newBanner, insertPositionMarker, src, options) ``` which should *return* the marked `fileContents`, i.e. the `fileContents` with all banner eligible for replacement removed and replaced by a simple `insertPositionMarker` string (see below). Your locate-and-match callback may insert multiple markers or none: `grunt-banner` will check how many markers you injected and either replace them when one(1) or more markers are seen, or revert to its basic `top|bottom` position-based banner *insertion* process when zero markers are found in your returned result. The callback function parameters: + `fileContents` (string) - the contents of the `src` file. + `newBanner` (string) - the new banner to be inserted by `grunt-banner`. This (and the `options` parameter, see below) allows you to customize `grunt-banner` behaviour to an extreme degree, even providing your own custom *replacer* entirely: simply return your processed result with a single marker and reduce the `options.banner` to an empty string. But I digress... + `insertPositionMarker` (string) - the insert marker. Currently this is the Unicode `REPLACEMENT CHARACTER` character, i.e. `\uFFFD`. We *assume* your original file content does not contain this marker already. + `src` (string) - the path to the file being processed. + `options` (object reference) - a *reference* to the current `options` object as used by `grunt-banner`. This **is not** the same as the options object you provided through your `Gruntfile`; this is a reference to the updated/augmented clone of that original as used by `grunt-banner` internally. Though the following coding practice should be frowned upon as 'side effects' are generally undesirable, you *can* tweak the `options.banner` value to suit your custom needs, for example. **Tread with great care when you are about to *edit* this object! The fact that you *can* doesn't mean you *should* fiddle with it!** ## The default locate-and-mark functionality The default locate-and-mark process, invoked when you specify the `replace: true` option or `position: "replace"` without any `replace:` value to go with that one, is set up to locate copyright comment chunks in either C or C++ style format, i.e. surrounded by `/*....*/` or single- or multiline `//` comment chunks. The process will inspect each comment chunk which start at the **left edge** (hence we ignore all *indented* comment chunks!) and which span *entire* lines, hence ruling out any comments which are leading or trailing source code statements *on the same line*. The last restriction placed on any 'old' banner to replace is that it **must** have the (case-**in**sensitive) word `Copyright` in there somewhere. And that word **must** be followed by a bit of non-whitespace blurb on the same line: generally a year, a name or both suffices to satisfy this last condition. Any such 'banner' block is marked for replacement in its entirety. **Warning Note**: The replacer *does not* check if the *new* banner also includes the `Copyright` phrase, hence multiple applications of `grunt-banner` may lead to the later rounds of `grunt-banner` application *adding* the shiny new banner at the top (or bottom) of the sourcefile! --- Gruntfile.js | 81 +++++++++++- tasks/usebanner.js | 112 ++++++++++++++--- test/banner_test.js | 116 +++++++++++++++--- test/expected/{some-banner.js => some.js} | 0 .../{some-bottom.js => someBottom.js} | 0 test/expected/someLineBreakTrue.js | 2 + test/expected/someMoreReplaceSmarts.js | 36 ++++++ test/expected/someMoreReplaceSmarts2.js | 34 +++++ test/expected/someReplaceDefaultstoTop.js | 8 ++ test/expected/someReplaceMultiple.js | 17 +++ test/expected/someReplaceSmart.js | 11 ++ test/expected/someReplaceSmartToBottom.js | 13 ++ test/expected/someReplaceToTop.js | 4 + ...{lineBreakTrue.js => someLineBreakTrue.js} | 0 test/fixtures/someMoreReplaceSmarts.js | 36 ++++++ test/fixtures/someMoreReplaceSmarts2.js | 36 ++++++ test/fixtures/someReplaceDefaultstoTop.js | 7 ++ test/fixtures/someReplaceMultiple.js | 17 +++ test/fixtures/someReplaceSmart.js | 21 ++++ test/fixtures/someReplaceSmartToBottom.js | 21 ++++ test/fixtures/someReplaceToTop.js | 3 + 21 files changed, 539 insertions(+), 36 deletions(-) rename test/expected/{some-banner.js => some.js} (100%) rename test/expected/{some-bottom.js => someBottom.js} (100%) create mode 100644 test/expected/someLineBreakTrue.js create mode 100644 test/expected/someMoreReplaceSmarts.js create mode 100644 test/expected/someMoreReplaceSmarts2.js create mode 100644 test/expected/someReplaceDefaultstoTop.js create mode 100644 test/expected/someReplaceMultiple.js create mode 100644 test/expected/someReplaceSmart.js create mode 100644 test/expected/someReplaceSmartToBottom.js create mode 100644 test/expected/someReplaceToTop.js rename test/fixtures/{lineBreakTrue.js => someLineBreakTrue.js} (100%) create mode 100644 test/fixtures/someMoreReplaceSmarts.js create mode 100644 test/fixtures/someMoreReplaceSmarts2.js create mode 100644 test/fixtures/someReplaceDefaultstoTop.js create mode 100644 test/fixtures/someReplaceMultiple.js create mode 100644 test/fixtures/someReplaceSmart.js create mode 100644 test/fixtures/someReplaceSmartToBottom.js create mode 100644 test/fixtures/someReplaceToTop.js diff --git a/Gruntfile.js b/Gruntfile.js index f7a2387..d455d0d 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -67,7 +67,7 @@ module.exports = function ( grunt ) { bannerReplace: { options: { position: 'replace', - replaceContent: '// replace-this-comment', + replace: '// replace-this-comment', banner: '// the banner' }, files: { @@ -75,6 +75,83 @@ module.exports = function ( grunt ) { } }, + bannerReplaceMultiple: { + options: { + position: 'replace', + replace: '// replace-this-comment', + banner: '// the banner' + }, + files: { + src: [ 'test/tmp/someReplaceMultiple.js'] + } + }, + + bannerReplaceToTop: { + options: { + position: 'top', + replace: '// replace-this-comment', + banner: '// the banner' + }, + files: { + src: [ 'test/tmp/someReplaceToTop.js'] + } + }, + + bannerReplaceDefaultsToTop: { + options: { + position: 'replace', + replace: '// replace-this-comment', + banner: '// the banner' + }, + files: { + src: [ 'test/tmp/someReplaceDefaultsToTop.js'] + } + }, + + bannerReplaceSmart: { + options: { + position: 'replace', + replace: true, + banner: '// the banner' + }, + files: { + src: 'test/tmp/someReplaceSmart.js' + } + }, + + bannerReplaceSmartMore: { + options: { + position: 'replace', + replace: '// replace-this-comment', + banner: '// the banner' + }, + files: { + src: 'test/tmp/someMoreReplaceSmarts.js' + } + }, + + bannerReplaceSmartMore2: { + options: { + position: 'replace', + replace: true, + banner: '// the banner' + }, + files: { + src: 'test/tmp/someMoreReplaceSmarts2.js' + } + }, + + bannerReplaceSmartToBottom: { + options: { + position: 'bottom', + replace: true, + banner: '// the banner' + }, + files: { + src: [ 'test/tmp/someReplaceSmartToBottom.js'] + } + }, + bannerNoLineBreak: { options: { banner: 'console.log("loaded"); ', @@ -91,7 +168,7 @@ module.exports = function ( grunt ) { linebreak: true }, files: { - src: [ 'test/tmp/lineBreakTrue.js' ] + src: [ 'test/tmp/someLineBreakTrue.js' ] } }, diff --git a/tasks/usebanner.js b/tasks/usebanner.js index d8fe3bd..30cf76c 100644 --- a/tasks/usebanner.js +++ b/tasks/usebanner.js @@ -12,6 +12,45 @@ var chalk = require ( 'chalk' ); module.exports = function ( grunt ) { + /* @const */ var insertPositionMarker = '\uFFFD'; // Unicode REPLACEMENT CHARACTER -- http://www.fileformat.info/info/unicode/char/fffd/index.htm + + function defaultOldBannerRemover(fileContents, newBanner, insertPositionMarker /* , src, options */) { + // Find a full-lines-spanning comment with the phrase `Copyright (c) ` in it, case-insensitive and `(c)` being optional. + // That will be our old banner and we kill the *entire* comment, it being C or C++ style, multiline or not. + // + // We only accept comments which start at column 1, i.e. at the left edge. Anything else is considered a minor - and thus irrelevant - comment. + + // Regex for the question: do we have one line in there which starts with `Copyright `? + // It's okay when it's preceded by some basic comment markers, but it MUST be followed by at *least* + // one(1) character of 'bla bla', whatever that blurb actually may be. + var copyright_re = /(^|\r\n|\n|\r)[\/*#|\s]*Copyright\s+[^\s\r\n]+/i; + // Regex for the question: do we have a one-or-many lines covering C comment? + var c_comment_re = /(^|\r\n|\n|\r)\/\*[^\0]*?\*\/\s*($|\r\n|\n|\r)/gi; + // Regex for the question: do we have a single or a whole *consecutive* bunch of `//` prefixed C++ style comment lines? + var cpp_comment_re = /(^|\r\n|\n|\r)(?:\/\/[^\n\r]*(?:\r\n|\n|\r))*\/\/[^\n\r]*($|\r\n|\n|\r)/gi; + + function check_n_replace(match, p1, p2) { + console.warn("test: match: ", match, "\n p1: ", p1, "\n p2: ", p2, "\n p3: ", arguments, "\nregex match: ", copyright_re.exec(match)); + if (copyright_re.test(match)) { + // got one! + return p1 + insertPositionMarker + p2; + } + // else: no dice! Do *not* alter: + return match; + } + + // We *do* expect the exceptional case of multiple old banners (and in different formats) + // to sit in the input file: we want to kill/replace them *all*! + // + // Hence we execute both regex replacements, irrespective of whether the first replace already + // delivered a hit. + // + // To emphasize: we want *all* the banners in there and kill/replace them *all*! + fileContents = fileContents.replace(c_comment_re, check_n_replace); + fileContents = fileContents.replace(cpp_comment_re, check_n_replace); + return fileContents; + } + // Please see the Grunt documentation for more information regarding task // creation: http://gruntjs.com/creating-tasks @@ -21,21 +60,34 @@ module.exports = function ( grunt ) { position: 'top', banner: '', linebreak: true, - process: false + process: false, + replace: false // boolean (true/false), string, RegExp or function which will filter the content before applying the new banner }); - if ( options.position !== 'top' && options.position !== 'bottom' && options.position !== 'replace') { + if ( options.position !== 'top' && options.position !== 'bottom' && options.position !== 'replace' ) { options.position = 'top'; } // Verify that if user wishes to replace content with a banner, that they have correctly // supplied the content they wish to replace. - if ( options.position === 'replace' ) { - if ( ! (('replaceContent' in options)) ) { - grunt.util.error('Detected option `replace` without accompanying option `replaceContent`.'); - } else if ( typeof options.replaceContent !== 'string' || ! (options.replaceContent instanceof RegExp) ) { - grunt.util.error('Detected option `replaceContent` with invalid type - type must be String or RegExp.'); + if ( options.replace ) { + switch ( typeof options.replace ) { + case 'boolean': + case 'string': + case 'function': + break; + + case 'object': + if ( options.replace instanceof RegExp ) { + break; + } + /* falls through */ + default: + grunt.util.error('Detected option `replace` with invalid type - type must be Boolean, String, RegExp or filter Function.'); + return; } + } else { + options.replace = (options.position === 'replace'); } var re = null; @@ -61,19 +113,43 @@ module.exports = function ( grunt ) { options.banner = options.process( src ); } - if ( options.position === 'replace' ) { - if ( ! (options.replaceContent instanceof RegExp) ) { - options.replaceContent = new RegExp(options.replaceContent); + var replacing_previous = false; + + if ( options.replace ) { + switch ( typeof options.replace ) { + case 'boolean': + fileContents = defaultOldBannerRemover(fileContents, options.banner, insertPositionMarker, src, options); + break; + + case 'function': + fileContents = options.replace(fileContents, options.banner, insertPositionMarker, src, options); + break; + + case 'string': + // Treat a String-type replace spec as an implicit *global* RexExp, spanning at least one(1) entire line: + options.replace = new RegExp(options.replace, 'g'); + /* falls through */ + case 'object': + //assert( options.replace instanceof RegExp ); + fileContents = fileContents.replace(options.replace, insertPositionMarker); + break; } - fileContents = fileContents.replace(options.replaceContent, options.banner); - grunt.file.write( src, fileContents ); - } else { - grunt.file.write( src, - options.position === 'top' ? - options.banner + linebreak + fileContents : - fileContents + linebreak + options.banner - ); + + replacing_previous = (fileContents.indexOf(insertPositionMarker) >= 0); } + + // - When `options.position === 'replace'` it is treated as 'top' when there's no banner to replace. + // + // - When `options.position` has another value (top|bottom) that setting will be adhered to + // and the existing banner(s) will be removed, while the new banner will be placed at the specified position. + var insertPositionMarker_re = new RegExp(insertPositionMarker, 'g'); + grunt.file.write( src, + ( options.position === 'replace' && replacing_previous ) ? + fileContents.replace(insertPositionMarker_re, options.banner /* + linebreak */ ) : + options.position !== 'bottom' ? + options.banner + linebreak + fileContents.replace(insertPositionMarker_re, '') : + fileContents.replace(insertPositionMarker_re, '') + linebreak + options.banner + ); grunt.verbose.writeln( 'Banner added to file ' + chalk.cyan( src ) ); } diff --git a/test/banner_test.js b/test/banner_test.js index 7347e51..2fdc03c 100644 --- a/test/banner_test.js +++ b/test/banner_test.js @@ -2,6 +2,13 @@ var grunt = require( 'grunt' ); +// make sure the files are loaded in a platform-agnostic way: we don't care about their +// line endings being CR, LF or CRLF: we all transform them to be UNIX LF-only. +function readFile( path ) { + var content = grunt.file.read( path ); + return content.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); +} + /* ======== A Handy Little Nodeunit Reference ======== https://github.com/caolan/nodeunit @@ -26,8 +33,8 @@ exports.banner = { bannerTop: function ( test ) { test.expect( 1 ); - var actual = grunt.file.read( 'test/tmp/some.js' ); - var expected = grunt.file.read( 'test/expected/some-banner.js' ); + var actual = readFile( 'test/tmp/some.js' ); + var expected = readFile( 'test/expected/some.js' ); test.equal( actual, expected, 'should add a banner to the top of a file' ); @@ -37,8 +44,8 @@ exports.banner = { bannerBottom: function ( test ) { test.expect( 1 ); - var actual = grunt.file.read( 'test/tmp/someBottom.js' ); - var expected = grunt.file.read( 'test/expected/some-bottom.js' ); + var actual = readFile( 'test/tmp/someBottom.js' ); + var expected = readFile( 'test/expected/someBottom.js' ); test.equal( actual, expected, 'should add a banner to the bottom of a file' ); @@ -48,30 +55,107 @@ exports.banner = { bannerReplace: function ( test ) { test.expect( 1 ); - var actual = grunt.file.read( 'test/tmp/someReplace.js' ); - var expected = grunt.file.read( 'test/expected/someReplace.js' ); + var actual = readFile( 'test/tmp/someReplace.js' ); + var expected = readFile( 'test/expected/someReplace.js' ); test.equal( actual, expected, 'should add a banner to replace content in the middle of a file' ); test.done(); }, + bannerReplaceMultiple: function ( test ) { + test.expect( 1 ); + + var actual = readFile( 'test/tmp/someReplaceMultiple.js' ); + var expected = readFile( 'test/expected/someReplaceMultiple.js' ); + + test.equal( actual, expected, 'should replace every banner in the file' ); + + test.done(); + }, + + bannerReplaceToTop: function ( test ) { + test.expect( 1 ); + + var actual = readFile( 'test/tmp/someReplaceToTop.js' ); + var expected = readFile( 'test/expected/someReplaceToTop.js' ); + + test.equal( actual, expected, 'should add a new banner at the top to replace the old banner in the middle of a file' ); + + test.done(); + }, + + bannerReplaceSmart: function ( test ) { + test.expect( 1 ); + + var actual = readFile( 'test/tmp/someReplaceSmart.js' ); + var expected = readFile( 'test/expected/someReplaceSmart.js' ); + + test.equal( actual, expected, 'should auto-detect and replace the banner in the middle of a file' ); + + test.done(); + }, + + bannerReplaceSmartMore: function ( test ) { + test.expect( 1 ); + + var actualNoMatch = readFile( 'test/tmp/someMoreReplaceSmarts.js' ); + var expectedNoMatch = readFile( 'test/expected/someMoreReplaceSmarts.js' ); + + test.equal( actualNoMatch, expectedNoMatch, 'should replace any comment that matches the specified banner string' ); + + test.done(); + }, + + bannerReplaceSmartMore2: function ( test ) { + test.expect( 1 ); + + var actualNoMatch = readFile( 'test/tmp/someMoreReplaceSmarts2.js' ); + var expectedNoMatch = readFile( 'test/expected/someMoreReplaceSmarts2.js' ); + + test.equal( actualNoMatch, expectedNoMatch, 'should not replace comments which look like banners but do not span entire lines, hence are NOT banners' ); + + test.done(); + }, + + bannerReplaceSmartToBottom: function ( test ) { + test.expect( 1 ); + + var actual = readFile( 'test/tmp/someReplaceSmartToBottom.js' ); + var expected = readFile( 'test/expected/someReplaceSmartToBottom.js' ); + + test.equal( actual, expected, 'should auto-detect and replace the banner in the middle of a file with a fresh one at the bottom' ); + + test.done(); + }, + bannerNoLineBreak: function ( test ) { test.expect( 1 ); - var actual = grunt.file.read( 'test/tmp/someNoLineBreak.js' ); - var expected = grunt.file.read( 'test/expected/someNoLineBreak.js' ); + var actual = readFile( 'test/tmp/someNoLineBreak.js' ); + var expected = readFile( 'test/expected/someNoLineBreak.js' ); test.equal( actual, expected, 'should add a banner without a linebreak' ); test.done(); }, + bannerLineBreak: function ( test ) { + test.expect( 1 ); + + var actual = readFile( 'test/tmp/someLineBreakTrue.js' ); + var expected = readFile( 'test/expected/someLineBreakTrue.js' ); + + test.equal( actual, expected, 'should add a banner with a linebreak' ); + + test.done(); + }, + bannerProcess: function ( test ) { test.expect( 1 ); - var actual = grunt.file.read( 'test/tmp/someProcess.js' ); - var expected = grunt.file.read( 'test/expected/someProcess.js' ); + var actual = readFile( 'test/tmp/someProcess.js' ); + var expected = readFile( 'test/expected/someProcess.js' ); test.equal( actual, expected, 'should add a banner with a custom process task for creating the banner' ); @@ -81,20 +165,20 @@ exports.banner = { bannerMatchPatternTop: function ( test ) { test.expect( 3 ); - var actualTop = grunt.file.read( 'test/tmp/someMatchingPatternTop.js' ); - var expectedTop = grunt.file.read( 'test/expected/someMatchingPatternTop.js' ); + var actualTop = readFile( 'test/tmp/someMatchingPatternTop.js' ); + var expectedTop = readFile( 'test/expected/someMatchingPatternTop.js' ); test.equal( actualTop, expectedTop, 'should add a banner to the top of a file if matching pattern' ); - var actualNoMatch = grunt.file.read( 'test/tmp/someNotMatchingPattern.js' ); - var expectedNoMatch = grunt.file.read( 'test/expected/someNotMatchingPattern.js' ); + var actualNoMatch = readFile( 'test/tmp/someNotMatchingPattern.js' ); + var expectedNoMatch = readFile( 'test/expected/someNotMatchingPattern.js' ); test.equal( actualNoMatch, expectedNoMatch, 'should not add a banner to the top of a file if not matching pattern' ); - var actualBottom = grunt.file.read( 'test/tmp/someMatchingPatternBottom.js' ); - var expectedBottom = grunt.file.read( 'test/expected/someMatchingPatternBottom.js' ); + var actualBottom = readFile( 'test/tmp/someMatchingPatternBottom.js' ); + var expectedBottom = readFile( 'test/expected/someMatchingPatternBottom.js' ); test.equal( actualBottom, expectedBottom, 'should add a banner to the bottom of a file if matching pattern' ); diff --git a/test/expected/some-banner.js b/test/expected/some.js similarity index 100% rename from test/expected/some-banner.js rename to test/expected/some.js diff --git a/test/expected/some-bottom.js b/test/expected/someBottom.js similarity index 100% rename from test/expected/some-bottom.js rename to test/expected/someBottom.js diff --git a/test/expected/someLineBreakTrue.js b/test/expected/someLineBreakTrue.js new file mode 100644 index 0000000..57c6896 --- /dev/null +++ b/test/expected/someLineBreakTrue.js @@ -0,0 +1,2 @@ +// Testing linebreak is true +var variable = "this is a variable" \ No newline at end of file diff --git a/test/expected/someMoreReplaceSmarts.js b/test/expected/someMoreReplaceSmarts.js new file mode 100644 index 0000000..552d294 --- /dev/null +++ b/test/expected/someMoreReplaceSmarts.js @@ -0,0 +1,36 @@ +// These next comments MAY look like matches but they really are NOT: they either +// don't *start* a line or *end* a line (yes, one in here is nasty as we only appended +// a single non-whitespace character at the end of that comment line! +// +// However, when specifying a simple *string* as `replace` value the plugin will consider +// that string to be a sufficient regex to pick only the banners you are looking for. +// +// Here we have constructed a case which exhibits the limitations of grunt-banner. +// +// NOT INTENEDED AS A BUG SCENARIO: THIS IS INTENDED (BACKWARD-COMPATIBLE) BEHAVIOUR. +// +// WHEN YOU WANT 'SMARTER' BEHAVIOUR EITHER SPECIFY `replace: true` TO LET THE DEFAULT +// LOCATE-AND-MARK FUNCTION HANDLE IT ALL or SPECIFY OUR OWN LOCATE-AND-MARK FUNCTION +// INSTEAD: `replace: myOwnCustomLocateAndMarkFunction`. + +var variable1 = "this is a variable"; +// the banner! +var variable2 = "this is another variable"; +var no_dont; // the banner +var variable3 = "this is another variable"; +/* replace-this-comment */ +var variable4 = "this is another variable"; +// whitespace before the comment IS acceptable, however. +// +// Hence *DO REPLACE* the next comment: + + // the banner +var variable5 = "this is another variable"; + + // Copyright some poor bugger +var variable5 = "this is another variable"; + +// Copyright some poor bugger +// +// Only this one will be considered a *real* banner by our default 'smart' locate-and-mark code. +var variable5 = "this is another variable"; diff --git a/test/expected/someMoreReplaceSmarts2.js b/test/expected/someMoreReplaceSmarts2.js new file mode 100644 index 0000000..5d9ba66 --- /dev/null +++ b/test/expected/someMoreReplaceSmarts2.js @@ -0,0 +1,34 @@ +// These next comments MAY look like matches but they really are NOT: they either +// don't *start* a line or *end* a line (yes, one in here is nasty as we only appended +// a single non-whitespace character at the end of that comment line! +// +// However, when specifying a simple *string* as `replace` value the plugin will consider +// that string to be a sufficient regex to pick only the banners you are looking for. +// +// Here we have constructed a case which exhibits the limitations of grunt-banner. +// +// NOT INTENEDED AS A BUG SCENARIO: THIS IS INTENDED (BACKWARD-COMPATIBLE) BEHAVIOUR. +// +// WHEN YOU WANT 'SMARTER' BEHAVIOUR EITHER SPECIFY `replace: true` TO LET THE DEFAULT +// LOCATE-AND-MARK FUNCTION HANDLE IT ALL or SPECIFY OUR OWN LOCATE-AND-MARK FUNCTION +// INSTEAD: `replace: myOwnCustomLocateAndMarkFunction`. + +var variable1 = "this is a variable"; +// replace-this-comment! +var variable2 = "this is another variable"; +var no_dont; // replace-this-comment +var variable3 = "this is another variable"; +/* replace-this-comment */ +var variable4 = "this is another variable"; +// whitespace before the comment IS acceptable, however. +// +// Hence *DO REPLACE* the next comment: + + // replace-this-comment +var variable5 = "this is another variable"; + + // Copyright some poor bugger +var variable5 = "this is another variable"; + +// the banner +var variable5 = "this is another variable"; diff --git a/test/expected/someReplaceDefaultstoTop.js b/test/expected/someReplaceDefaultstoTop.js new file mode 100644 index 0000000..a0c647d --- /dev/null +++ b/test/expected/someReplaceDefaultstoTop.js @@ -0,0 +1,8 @@ +// the banner +// do not find any suitable banner to replace in here +var variable1 = "this is a variable"; +// do not replace this comment + +// DO NOT replace-this-comment +var variable2 = "this is another variable"; +// do not find any suitable banner to replace in here diff --git a/test/expected/someReplaceMultiple.js b/test/expected/someReplaceMultiple.js new file mode 100644 index 0000000..9ad8a6e --- /dev/null +++ b/test/expected/someReplaceMultiple.js @@ -0,0 +1,17 @@ +var variable1 = "this is a variable"; +// the banner +var variable2 = "this is another variable"; +// the banner +var variable3 = "this is another variable"; +// DO NOT replace-this-comment +var variable4 = "this is another variable"; +// the banner +var variable5 = "this is another variable"; +// the banner! +var variable6 = "this is another variable"; + // the banner +var variable7 = "this is another variable"; +// the banner +// +// and keep this bit intact, though it is a cluster of comment lines... +var variable7 = "this is another variable"; diff --git a/test/expected/someReplaceSmart.js b/test/expected/someReplaceSmart.js new file mode 100644 index 0000000..4ec2c12 --- /dev/null +++ b/test/expected/someReplaceSmart.js @@ -0,0 +1,11 @@ +// do-not-replace-this-comment +var variable1 = "this is a variable"; +// do-not-replace-this-comment + +// the banner +var variable2 = "this is another variable"; +// do-not-replace-this-comment + +// the banner +var variable2 = "this is another variable"; +// do-not-replace-this-comment diff --git a/test/expected/someReplaceSmartToBottom.js b/test/expected/someReplaceSmartToBottom.js new file mode 100644 index 0000000..fa342be --- /dev/null +++ b/test/expected/someReplaceSmartToBottom.js @@ -0,0 +1,13 @@ +// do-not-replace-this-comment +var variable1 = "this is a variable"; +// do-not-replace-this-comment + + +var variable2 = "this is another variable"; +// do-not-replace-this-comment + + +var variable2 = "this is another variable"; +// do-not-replace-this-comment + +// the banner \ No newline at end of file diff --git a/test/expected/someReplaceToTop.js b/test/expected/someReplaceToTop.js new file mode 100644 index 0000000..736b212 --- /dev/null +++ b/test/expected/someReplaceToTop.js @@ -0,0 +1,4 @@ +// the banner +var variable1 = "this is a variable"; + +var variable2 = "this is another variable"; \ No newline at end of file diff --git a/test/fixtures/lineBreakTrue.js b/test/fixtures/someLineBreakTrue.js similarity index 100% rename from test/fixtures/lineBreakTrue.js rename to test/fixtures/someLineBreakTrue.js diff --git a/test/fixtures/someMoreReplaceSmarts.js b/test/fixtures/someMoreReplaceSmarts.js new file mode 100644 index 0000000..b94e9fe --- /dev/null +++ b/test/fixtures/someMoreReplaceSmarts.js @@ -0,0 +1,36 @@ +// These next comments MAY look like matches but they really are NOT: they either +// don't *start* a line or *end* a line (yes, one in here is nasty as we only appended +// a single non-whitespace character at the end of that comment line! +// +// However, when specifying a simple *string* as `replace` value the plugin will consider +// that string to be a sufficient regex to pick only the banners you are looking for. +// +// Here we have constructed a case which exhibits the limitations of grunt-banner. +// +// NOT INTENEDED AS A BUG SCENARIO: THIS IS INTENDED (BACKWARD-COMPATIBLE) BEHAVIOUR. +// +// WHEN YOU WANT 'SMARTER' BEHAVIOUR EITHER SPECIFY `replace: true` TO LET THE DEFAULT +// LOCATE-AND-MARK FUNCTION HANDLE IT ALL or SPECIFY OUR OWN LOCATE-AND-MARK FUNCTION +// INSTEAD: `replace: myOwnCustomLocateAndMarkFunction`. + +var variable1 = "this is a variable"; +// replace-this-comment! +var variable2 = "this is another variable"; +var no_dont; // replace-this-comment +var variable3 = "this is another variable"; +/* replace-this-comment */ +var variable4 = "this is another variable"; +// whitespace before the comment IS acceptable, however. +// +// Hence *DO REPLACE* the next comment: + + // replace-this-comment +var variable5 = "this is another variable"; + + // Copyright some poor bugger +var variable5 = "this is another variable"; + +// Copyright some poor bugger +// +// Only this one will be considered a *real* banner by our default 'smart' locate-and-mark code. +var variable5 = "this is another variable"; diff --git a/test/fixtures/someMoreReplaceSmarts2.js b/test/fixtures/someMoreReplaceSmarts2.js new file mode 100644 index 0000000..b94e9fe --- /dev/null +++ b/test/fixtures/someMoreReplaceSmarts2.js @@ -0,0 +1,36 @@ +// These next comments MAY look like matches but they really are NOT: they either +// don't *start* a line or *end* a line (yes, one in here is nasty as we only appended +// a single non-whitespace character at the end of that comment line! +// +// However, when specifying a simple *string* as `replace` value the plugin will consider +// that string to be a sufficient regex to pick only the banners you are looking for. +// +// Here we have constructed a case which exhibits the limitations of grunt-banner. +// +// NOT INTENEDED AS A BUG SCENARIO: THIS IS INTENDED (BACKWARD-COMPATIBLE) BEHAVIOUR. +// +// WHEN YOU WANT 'SMARTER' BEHAVIOUR EITHER SPECIFY `replace: true` TO LET THE DEFAULT +// LOCATE-AND-MARK FUNCTION HANDLE IT ALL or SPECIFY OUR OWN LOCATE-AND-MARK FUNCTION +// INSTEAD: `replace: myOwnCustomLocateAndMarkFunction`. + +var variable1 = "this is a variable"; +// replace-this-comment! +var variable2 = "this is another variable"; +var no_dont; // replace-this-comment +var variable3 = "this is another variable"; +/* replace-this-comment */ +var variable4 = "this is another variable"; +// whitespace before the comment IS acceptable, however. +// +// Hence *DO REPLACE* the next comment: + + // replace-this-comment +var variable5 = "this is another variable"; + + // Copyright some poor bugger +var variable5 = "this is another variable"; + +// Copyright some poor bugger +// +// Only this one will be considered a *real* banner by our default 'smart' locate-and-mark code. +var variable5 = "this is another variable"; diff --git a/test/fixtures/someReplaceDefaultstoTop.js b/test/fixtures/someReplaceDefaultstoTop.js new file mode 100644 index 0000000..74be7ba --- /dev/null +++ b/test/fixtures/someReplaceDefaultstoTop.js @@ -0,0 +1,7 @@ +// do not find any suitable banner to replace in here +var variable1 = "this is a variable"; +// do not replace this comment + +// DO NOT replace-this-comment +var variable2 = "this is another variable"; +// do not find any suitable banner to replace in here diff --git a/test/fixtures/someReplaceMultiple.js b/test/fixtures/someReplaceMultiple.js new file mode 100644 index 0000000..c7f3f4a --- /dev/null +++ b/test/fixtures/someReplaceMultiple.js @@ -0,0 +1,17 @@ +var variable1 = "this is a variable"; +// replace-this-comment +var variable2 = "this is another variable"; +// replace-this-comment +var variable3 = "this is another variable"; +// DO NOT replace-this-comment +var variable4 = "this is another variable"; +// replace-this-comment +var variable5 = "this is another variable"; +// replace-this-comment! +var variable6 = "this is another variable"; + // replace-this-comment +var variable7 = "this is another variable"; +// replace-this-comment +// +// and keep this bit intact, though it is a cluster of comment lines... +var variable7 = "this is another variable"; diff --git a/test/fixtures/someReplaceSmart.js b/test/fixtures/someReplaceSmart.js new file mode 100644 index 0000000..914d5bc --- /dev/null +++ b/test/fixtures/someReplaceSmart.js @@ -0,0 +1,21 @@ +// do-not-replace-this-comment +var variable1 = "this is a variable"; +// do-not-replace-this-comment + +// Here's the old banner: +// +// Copyright (c) 2015 we-dont-care +// +// More banner blurb... +var variable2 = "this is another variable"; +// do-not-replace-this-comment + +/*! + * And we go nuts: here's *another* old banner: + * + * Copyright (c) 2015 we-dont-care ... again! + * + * More banner blurb... + */ +var variable2 = "this is another variable"; +// do-not-replace-this-comment diff --git a/test/fixtures/someReplaceSmartToBottom.js b/test/fixtures/someReplaceSmartToBottom.js new file mode 100644 index 0000000..914d5bc --- /dev/null +++ b/test/fixtures/someReplaceSmartToBottom.js @@ -0,0 +1,21 @@ +// do-not-replace-this-comment +var variable1 = "this is a variable"; +// do-not-replace-this-comment + +// Here's the old banner: +// +// Copyright (c) 2015 we-dont-care +// +// More banner blurb... +var variable2 = "this is another variable"; +// do-not-replace-this-comment + +/*! + * And we go nuts: here's *another* old banner: + * + * Copyright (c) 2015 we-dont-care ... again! + * + * More banner blurb... + */ +var variable2 = "this is another variable"; +// do-not-replace-this-comment diff --git a/test/fixtures/someReplaceToTop.js b/test/fixtures/someReplaceToTop.js new file mode 100644 index 0000000..ea4b65b --- /dev/null +++ b/test/fixtures/someReplaceToTop.js @@ -0,0 +1,3 @@ +var variable1 = "this is a variable"; +// replace-this-comment +var variable2 = "this is another variable"; \ No newline at end of file