5

I know I can setup a task in grunt.config that will grunt-contrib-copy files from src to dest, and looking through Grunt docs I know I can copy single files using grunt.file.copy.

But, is it possible to create grunt-contrib-copy tasks on the fly in a custom registered task to accommodate arguments being sent from the bash? I want to create a new directory grunt.file.mkdir("some/path/appfoldername") and then copy files to that folder from another destination, but I won't know the folder name until after the custom task is run. So something like:

grunt create_app:appfoldername

So I could copy single files over one at a time, but the files will change so want the power of grunt-contrib-copy, but with the flexibility of a custom registered task.

I imagine something like this if my explanation isn't clear enough:

grunt.registerTask('createCopy', 'Create a copy', function(folderName) {

    var cwd = grunt.template.process("some/path/to/app");
    var src = grunt.template.process("some/path/to/src");
    var dest = grunt.template.process("some/path/to/dest") + folderName;

    grunt.task.run('copy: {  
       files: { 
          cwd: cwd,
          src: src,
          dest: dest
       }           
    }');
}

UPDATE

grunt.registerTask('mkapp', 'Create Application', function(appName) {

    // Check appName is not empty or undefined
    if(appName !== "" && appName !== undefined) { 

        // Require node path module, since not a global
        var path = require("path");

        // Resolve directory path using templates
        var relPath = grunt.template.process("<%= root %>/<%= dirs.src %>/application/");
        var srcPath = relPath + "default_install";
        var destPath = relPath + appName;

        // Create new application folder
        grunt.file.mkdir(destPath, 0444);

        // Return unique array of all file paths which match globbing pattern
        var options = { cwd: srcPath, filter: 'isFile' };
        var globPattern = "**/*"

        grunt.file.expand(options, globPattern).forEach(function(srcPathRelCwd) {

            // Copy a source file to a destination path, creating directories if necessary
            grunt.file.copy(
                path.join(srcPath, srcPathRelCwd), 
                path.join(destPath, srcPathRelCwd)
            );
        });
    }
});
mtpultz
  • 17,267
  • 22
  • 122
  • 201

2 Answers2

1

Yes. Just call grunt.file.expand on the file specs and then loop over the resulting array, copying them however you like.

grunt.file.expand(specs).forEach(function(src) {
  grunt.file.copy(src, dest);
});
raidendev
  • 2,729
  • 1
  • 22
  • 23
ryanb
  • 934
  • 7
  • 4
  • Hi @ryanb, I've tried a couple variations, but I can't figure out what the options should be set to? Even using http://gruntjs.com/api/grunt.file#grunt.file.expand so I added my code above in case that helps. – mtpultz Aug 05 '14 at 02:48
  • If I remove grunt.file.copy(src, destPath) and leave the writeln() it displays all the file paths, and I can write out the destPath as well. – mtpultz Aug 05 '14 at 03:00
  • It appears not to like the folders? Keeps saying "Warning: Unable to read 'cache' file (Error code: ENOENT).", but when I remove the cache folder it then throws an error on the next folder config. – mtpultz Aug 05 '14 at 03:05
  • Got it! Phew, posting result in update above for anyone that wants to duplicate this solution. Thanks to @ryanb – mtpultz Aug 05 '14 at 03:26
1

If you want this to work cross-platform, you can use Node's path API to create the file paths with correct path separators and such.

grunt.file.expand(options, globPattern).forEach(function(srcPathRelCwd) {
    // Copy a source file to a destination path, creating directories if necessary
    grunt.file.copy(
        path.join(srcPath, srcPathRelCwd), 
        path.join(destPath, srcPathRelCwd)
    );
});
ryanb
  • 934
  • 7
  • 4
  • and don't forget that join is not a global module, and it needs to be required using var path = require("path"); – mtpultz Aug 08 '14 at 23:07