How can I rename files with Grunt, based on the respective file's parent folder name?
Asked Answered
G

6

52

I have a the following structure:

src/
    modules/
        module1/
            js/
                main.js
            scss/
                main.scss
            index.html
        module2/
            js/
                main.js
            scss/
                main.scss
            index.html

I'd like to run a grunt task to copy these out to the following structure:

dev/
    js/
        module1.js
        module2.js
    css/
        module1.css
        module2.css
    module1.html
    module2.html

Is there a way to do this with an existing grunt plugin? If not, how could I achieve this?

Glair answered 7/3, 2013 at 12:20 Comment(0)
O
91

This can be done using the grunt-contrib-copy plugin.

The main thing to note is that you can change the destination programmatically by using a rename function (which takes in the destination and source of each file).

Here is a (somewhat brittle) sample Gruntfile.js that should copy to your desired structure:

module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    copy: {
      main: {
        files: [
          {
            expand: true, 
            cwd: 'src/modules/', 
            src: ['**/*.js'], 
            dest: 'dev/js/', 
            rename: function(dest, src) {
              // use the source directory to create the file
              // example with your directory structure
              //   dest = 'dev/js/'
              //   src = 'module1/js/main.js'
              return dest + src.substring(0, src.indexOf('/')) + '.js';
            }
          },
          {
            expand: true, 
            cwd: 'src/modules/', 
            src: ['**/*.scss'], 
            dest: 'dev/css/', 
            rename: function(dest, src) {
              return dest + src.substring(0, src.indexOf('/')) + '.css';
            }
          },
          {
            expand: true, 
            cwd: 'src/modules/', 
            src: ['**/*.html'], 
            dest: 'dev/', 
            rename: function(dest, src) {
              return dest + src.substring(0, src.indexOf('/')) + '.html';
            }
          }
        ]
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-copy');

  // Default task(s).
  grunt.registerTask('default', ['copy']);
};
Oleson answered 20/3, 2013 at 6:23 Comment(9)
Got reverse situation: needed to move files to different folders from one source. Your answer solved my problem, thanks!Undervest
also very important to note here is that if you want to skip certain files while renaming, the correct return value (the default) is return dest+'/'+src - which will cause the file to be copied to the default location.Leap
So, just to do a simple command line operation like copying files, you have to install a plugin? Do you have to install a plugin for basically every operation?Maryrose
@Jakobud no you can use built-in Node.js methods for filehandling, see nodejs.org/api/fs.html although they are just low levelSalpiglossis
In my case, I also use the rename as a filter function, and when encountering files I do not want to copy, I send them to '/tmp/'+dest+'/'+src. Neglecting to return a value from the rename function causes an error.Maddalena
It seems this doesn't work anymore. There is no "rename" option in the current version (0.7.0) of grunt-conrib-copyMadelina
@Madelina I believe the rename functionality is actually a feature of grunt itself (and not specific to grunt-contrib-copy). I tested with [email protected] and [email protected] and the rename function still gets called for me. Here is the code in grunt that runs the rename function. Let me know if there is an easy way to reproduce the issue you're having!Oleson
@Oleson Thanks for the clarification! I tried it again and it works... I don't know what I did wrong the first time... probably some config mistakeMadelina
My mistake was that I put "rename" under the "options" object for "copy", when it belonged in the top level object. Maybe that will help somebody :-).Canker
F
6

There's no need to use grunt-contrib-copy just for this any more, you can now take advantage of grunt.file.expandMapping which has options to just change the file extension, or to define a function that returns the output filename.

Here's an example of a files object in a jade task for compiling .jade templates into .html files:

files: [{
    expand: true, 
    src: "**/*.jade", 
    dest: "<%= distDir %>", 
    cwd: "<%= assetsDir %>/jade", 
    rename: function(dest, matchedSrcPath, options) {
        // return the destination path and filename:
        return (dest + matchedSrcPath).replace('.jade', '.html');
    }
}]

It would have been easier to use the ext: '.html' option instead of the rename option in this case, but I'm using rename here so you can see how it works.

More info about the ext and rename (and other) options in the grunt.file docs. Some more examples here and here.

Flying answered 18/1, 2015 at 15:4 Comment(0)
P
2

You could simply use the options: expand : true, flatten: true

No need for custom rename callbacks.

Petigny answered 15/7, 2014 at 9:58 Comment(0)
N
1

if u want to rename .coffee files to .js or similar then just adapt it ;)

sudo npm install grunt-contrib-copy

copy: {
  rename: {
    files: [{
     expand: true,
     dot: true,
     cwd: './app/scripts',
     dest: './app/scripts/',
     src: [
       '**/*.coffee'
     ],
     rename: function(dest, src) {
       console.log(dest + src);
       return dest + src.replace('.coffee','.js');
     }
   }]
  }
},
Nuisance answered 22/6, 2018 at 11:55 Comment(0)
A
0

Not exactly an answer to your question but i made it here looking for relative dest folders with grunt so... Here is how i solved it

...
base: {
  files:
  [
    {
      expand: true,
      cwd: 'app/design/frontend/',
      src: ['**/Magento_Sales/email-src/*.html'],
      dest: '../../Magento_Sales/email/',
      rename: function(dest, src, expand) {
        src = path.parse(src)
        return path.join(expand.cwd, src.dir, dest, src.base);
      }
    },
  ],
}
...

This little bit path.join(expand.cwd, src.dir, dest, src.base); just creating the path i need.

expand.cwd = app/design/frontend/

src.dir = <DYNAMIC_FOLDERS>/Magento_Sales/email-src/

dest = ../../Magento_Sales/email/

src.base = <FILE>.html

and all together it = app/design/frontend/<COMPANY>/<MAIN-THEME>/Magento_Sales/email/<FILE>.html

and in my situation it will now compile my html emails in relative destination folders

Addiel answered 30/8, 2017 at 4:10 Comment(0)
H
0

I wanted to save the file in a min folder created at the root where we found the file so it's more easy to moove the plugin folder into another project :

-- Plugins/    
     -- bootstrap/                
            -- bootstrap.js
            -- bootstrap.css
            -- min/
                 -- bootstrap.min.js
                 -- bootstrap.min.css

I post the solution I found thanks to this page maybe it can help others :

uglify: {
    options: {
        mangle: true
    },
    dynamic_mappings: {
        // Grunt will search for "**/*.js" under "Plugins/" when the "uglify" task
        // runs and build the appropriate src-dest file mappings then, so you
        // don't need to update the Gruntfile when files are added or removed.
        files: [
            {
                expand: true,     // Enable dynamic expansion.
                cwd: 'Plugins/',  // Src matches are relative to this path.
                src: ['**/*.js', '!**/*.min.js'], // Pattern to match (ignore .min files)
                dest: '/min/',     // Destination path prefix.
                ext: '.min.js',   // Dest filepaths will have this extension.
                extDot: 'last',   // Extensions in filenames begin after the first dot
                rename: function( dest, matchedPath, options ) {

                    Pathname = matchedPath.substring( 0, matchedPath.indexOf('/') );
                    Filename = matchedPath.replace( Pathname, '' );

                    //Return "Plugins / plugin_folder / min / file_name.min.js"
                    return options.cwd + Pathname + dest + Filename;
                }
            },
        ],
    },
}
Hurd answered 20/3, 2021 at 6:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.