How can I rename files with Grunt, based on the respective file's parent folder name?
GruntjsParent ChildRenameBuild AutomationGruntjs Problem Overview
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?
Gruntjs Solutions
Solution 1 - Gruntjs
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']);
};
Solution 2 - Gruntjs
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.
Solution 3 - Gruntjs
You could simply use the options: expand : true, flatten: true
No need for custom rename callbacks.
Solution 4 - Gruntjs
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
Solution 5 - Gruntjs
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');
}
}]
}
},
Solution 6 - Gruntjs
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;
}
},
],
},
}