/*
 * grunt-neuter
 *
 * Copyright (c) 2012 Trek Glowacki, contributors.
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {
  grunt.registerMultiTask('neuter', 'Concatenate files in the order you require', function() {
    // track required files for this task.
    // once a file has been required it will be added to this array
    // which will be checked before attempting to add a file.
    // each file can be required only once.
    var required = [];

    // the bufffer that we appened to over this run. 
    var out = [];

    // matches `require('some/path/file');` statements.
    // no need to include a .js as this will be appended for you.
    var requireSplitter = /(require\([\'||\"].*[\'||\"]\));+\n*/;
    var requireMatcher = /require\([\'||\"](.*)[\'||\"]\)/;

    var options = this.options({
      template: "(function() {\n\n<%= src %>\n\n})();",
      separator: "\n\n",
      includeSourceURL: false
    });

    var finder = function(filepath){
      if (!grunt.file.exists(filepath)) {
        grunt.log.error('Source file "' + filepath + '" not found.');
        return '';
      }

      // once a file has been required its source will 
      // never be written to the resulting destination file again.
      if (required.indexOf(filepath) === -1) {
        required.push(filepath);

        // read the file and split it into code sections
        // these will be either require(...) statements
        // or blocks of code.
        var src = grunt.file.read(filepath);
        var sections = src.split(requireSplitter);
        
        // loop through sections appending to out buffer.
        sections.forEach(function(section){
          if (!section.length) { return; }

          // if the section is a require statement
          // recursively call find again. Otherwise
          // push the code section onto the buffer.
          var match = requireMatcher.exec(section);
          if (match) {
            finder(match[1] + '.js');
          } else {
            out.push({filepath: filepath, src: section});
          }
        });
      }
    };

    // kick off the process. Find code sections, combine them
    // in the correct order by wrapping in the template
    // which defaults to a functional closure.
    grunt.file.expand({nonull: true}, this.file.srcRaw).map(finder, this);
    var outStr = out.map(function(section){
      var templateData = {
        data: section
      };

      if (options.includeSourceURL) {
        return "eval(" + JSON.stringify(grunt.template.process(options.template, templateData) + "//@ sourceURL=" + section.filepath) +")";
      } else {
        return grunt.template.process(options.template, templateData);
      }
    }).join(options.separator);

    grunt.file.write(this.file.dest, outStr);
  });
};

@@ -2292,16 +2292,58 @@
 losure.%0A
+    this.files.forEach(function(file) %7B%0A  
     grun
@@ -2376,21 +2376,16 @@
 e%7D, 
-this.
 file.src
 Raw)
@@ -2384,11 +2384,8 @@
 .src
-Raw
 ).ma
@@ -2401,16 +2401,18 @@
  this);%0A
+  
     var 
@@ -2443,24 +2443,26 @@
 n(section)%7B%0A
+  
       var te
@@ -2484,16 +2484,18 @@
         
+  
 data: se
@@ -2502,28 +2502,32 @@
 ction%0A      
+  
 %7D;%0A%0A
+  
       if (op
@@ -2548,24 +2548,26 @@
 ourceURL) %7B%0A
+  
         retu
@@ -2694,16 +2694,18 @@
 ) +%22)%22;%0A
+  
       %7D 
@@ -2715,24 +2715,26 @@
 e %7B%0A        
+  
 return grunt
@@ -2786,26 +2786,30 @@
 ata);%0A      
-%7D%0A
+  %7D%0A  
     %7D).join(
@@ -2829,24 +2829,26 @@
 ator);%0A%0A    
+  
 grunt.file.w
@@ -2852,21 +2852,16 @@
 e.write(
-this.
 file.des
@@ -2872,16 +2872,24 @@
 utStr);%0A
+    %7D);%0A
   %7D);%0A%7D;

Update to work with grunt v0.4.0rc5.