I would like to configure and execute a task that copies a specified file in each dir recursively. In specific I would like to have "index.php" copied in each directory and subdirectory of my web project, if that doesn't exist. I've tried to use multidest and copy but multidestdoesn't seem to allow to specify paths by means of a wildcard (too bad!). How could I solve this? Thank you
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
multidest: {
tst: {
tasks: ['copy:indexphp'],
dest: '**/' //THIS DOESN'T WORK, I SHOULD SPECIFY ANY DIR/SUBDIR/ETC..
},
},
copy: {
indexphp: {
expand: true,
cwd: 'classes',
src: 'index.php',
filter: function (filepath) {
// NPM load file path module.
var path = require('path');
// Construct the destination file path.
var dest = path.join(
grunt.task.current.data.dest,
path.basename(filepath)
);
// Return false if the file exists.
return !(grunt.file.exists(dest));
}
},
[...]
Related
I am trying to add a new grunt task in a sails.js application.
To keep my test case simple i have copied the copy task to a new file copy-tests.js, which is also in the tasks/config folder.
I have modified copy-tests.js to update the first param of the set method to 'copy-tests', so it now reads
module.exports = function(grunt) {
grunt.config.set('copy-tests', {
dev: {
files: [{
expand: true,
cwd: './assets',
src: ['**/*.!(coffee|less)'],
dest: '.tmp/public'
}]
},
build: {
files: [{
expand: true,
cwd: '.tmp/public',
src: ['**/*'],
dest: 'www'
}]
}
});
grunt.loadNpmTasks('grunt-contrib-copy');
};
I have updated compileAssets.js to add my new tasks, so it now reads
module.exports = function (grunt) {
grunt.registerTask('compileAssets', [
'clean:dev',
'jst:dev',
'less:dev',
'copy:dev',
'copy-tests:dev',
'coffee:dev',
'jade:dev'
]);
};
When i try to lift the sails app i get 'Task "copy-tests:dev" not found'
I know that grunt can see my file as i have added a log statement to Gruntfile.js as follows
var taskConfigurations = loadTasks('./tasks/config'),
registerDefinitions = loadTasks('./tasks/register');
console.log(taskConfigurations);
I can see copy-tests in among the other tasks when this statement logs
Can anyone help with why grunt claims it can't find copy-tests:dev ?
Alternatively maybe i am going about this the wrong way. What i am trying to achieve is having two copy tasks, one to copy my main build content to the .tmp folder, and a second one to copy my client side tests to the .tmp folder. Later i will make sure that only my dev builds run both copy tasks, my prod one will only run the first one. I thought the best way to do this would be with two grunt tasks in the config folder, but maybe there is another way...
Rather than define a second task to have an alternative copy, i needed to add to the Copy task as follows
module.exports = function(grunt) {
grunt.config.set('copy', {
dev: {
files: [{
expand: true,
cwd: './assets',
src: ['**/*.!(coffee|less)'],
dest: '.tmp/public'
}]
},
devTests: {
files: [{
expand: true,
cwd: './tests',
src: ['**/*.*'],
dest: '.tmp/public/tests'
}]
},
build: {
files: [{
expand: true,
cwd: '.tmp/public',
src: ['**/*'],
dest: 'www'
}]
}
});
grunt.loadNpmTasks('grunt-contrib-copy');
};
My compileAssets then looks like
module.exports = function (grunt) {
grunt.registerTask('compileAssets', [
'clean:dev',
'jst:dev',
'less:dev',
'copy:dev',
'copy:devTests',
'coffee:dev',
'jade:dev'
]);
};
I realised this when i looked at the sails-linker.js task and saw how many targets there were in that. I had been thinking of the targets as build targets and assuming they would have values like dev, test, prod etc, but it appears that they are a broader concept and every type of copy i might want to do should be a target in the copy.js task.
I'm working on a grunt script to copy some files from 'dev' into 'dist' and modify the folder structure slightly. There are html files present inside dev/html/eas1/ and dev/html/eas2. The script uses grunt.config.set in order to change the src and dest locations of the copy and the script is running without errors, but nothing is being copied over. Can anyone help?
module.exports = function(grunt) {
grunt.initConfig({
projectCodes: {
eas: ['eas1', 'eas2', 'eas3', 'eas4', 'eas5', 'eas6']
},
copy: {
main: {
files: [{
expand: true,
src: 'dev/eas/html/eas1/*',
dest: 'dist/eas/eas1/html/',
filter: 'isFile'
}]
}
}
});
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.registerTask('dev', function(p) {
var projectCodes = grunt.config.get('projectCodes');
for(code in projectCodes[p]) {
grunt.config.set('copy.main.files.0.src', 'dev/'+p+'/html/'+projectCodes[p][code]+'/*');
grunt.config.set('copy.main.files.0.dest', 'dist/'+p+'/'+projectCodes[p][code]+'/html/');
grunt.task.run('copy:main');
}
});
};
I am using a configuration file with JSON format containing names of other files which I concatenate when one of the files is changed. It works great so far, but I want to make the Grunt to reload the configuration file once it's changed (I remove or add something to the JSON) and update other task options (the concat task).
Here is my simplified Gruntfile.js:
module.exports = function(grunt) {
function getModules() {
var modules = grunt.file.readJSON('src/js/modules.json').modules;
for ( var module in modules ) {
if ( modules.hasOwnProperty(module) ) {
modules[module] = 'src/js/modules/' + modules[module] + '.js';
}
}
modules.push('src/js/scripts.js');
return modules;
}
var modules = getModules();
grunt.initConfig({
concat: {
options: {
separator: ';',
},
dist: {
src: modules,
dest: 'assets/js/scripts.min.js',
},
},
watch: {
js_modules: {
files: ['src/js/modules.json'],
tasks: ['reload_modules', 'concat'],
options: {
spawn: false,
livereload: true,
},
}
}
});
grunt.registerTask('reload_modules', "Reload JavaScript modules", function() {
modules = getModules();
});
};
As you can see I have some attempt to solve my problem, but the updated modules variable is not used in the concat task. The task uses the variable value loaded when the grunt default task is started.
You should be able to overwrite the config using grunt.config.merge:
var config = {
// ...
};
grunt.config.init(config);
grunt.registerTask('reload_modules', "Reload JavaScript modules", function() {
config.concat.options.dist.src = getModules();
grunt.config.merge(config);
});
http://gruntjs.com/api/grunt.config
The following code reads the contents of every subdirectory js inside of app/modules/ (eg. app/modules/module1/js/, app/modules/module2/js/, aso.)
this script worked before WITHOUT using the last command grunt.task.run('concat:' + dir);.
for a while now it stopped working so that i had to add a call to the task concat inside of the forEach loop.
normally I would have saved the new configuration inside of the concat configuration and call sometime later the resulting concat task.
grunt.registerTask('preparemodulejs', 'iterates over all module directories and compiles modules js files', function() {
// read all subdirectories from your modules folder
grunt.file.expand('./app/modules/*').forEach(function(dir){
// get the current concat config
var concat = grunt.config.get('concat') || {};
// set the config for this modulename-directory
concat[dir] = {
src: [dir + '/js/*.js', '!' + dir + '/js/compiled.js'],
dest: dir + '/js/compiled.js'
};
// save the new concat config
grunt.config.set('concat', concat);
grunt.task.run('concat:' + dir); // this line is new
});
});
what exactly changed in recent versions that i have to add an explicit task.run line?
and is there any way to write the settings of this task into the settings of an existing concat task so that if i have other manual additions to that configuration those won't run for each directory scanned?
thanks for help.
grunt.task.run(); despite it's name, does not run tasks. Grunt is always synchronous so grunt.task.run() will queue tasks to run after the current task has finished.
So I would avoid using grunt.task.run() within an array but rather build a list of tasks/targets to run afterward:
grunt.registerTask('preparemodulejs', 'iterates over all module directories and compiles modules js files', function() {
var tasks = [];
// read all subdirectories from your modules folder
grunt.file.expand('./app/modules/*').forEach(function(dir){
// get the current concat config
var concat = grunt.config.get('concat') || {};
// set the config for this modulename-directory
concat[dir] = {
src: [dir + '/js/*.js', '!' + dir + '/js/compiled.js'],
dest: dir + '/js/compiled.js'
};
// save the new concat config
grunt.config.set('concat', concat);
tasks.push('concat:' + dir);
});
// queues the tasks and run when this current task is done
grunt.task.run(tasks);
});
We can also provide a config directly here for different tasks to run on the go for bigger projects having multiple modules. Even if the we need to process files outside of the root directory:
grunt.registerTask('publishapp', 'uglify ivapp.js and upload to server', function (){
var tasks = [];
grunt.file.expand('../outerdirectory/').forEach(function(dir) {
// config for uglify that needs to execute before uploading on server
var uglify = {
options: {
compress: {
drop_console: true,
},
banner: '/* Banner you want to put above js minified code. */\n'
},
all: {
files: [{
expand: true,
cwd: '../',
src: ['somedir/some.js'],
dest: 'build',
ext: '.js',
extDot: 'last'
}]
}
};
// set grunt config : uglify
grunt.config.set('uglify', uglify);
});
// prepare a tasks list
tasks.push('uglify:all');
tasks.push('exec:publish');
// execute a tasks to perform
grunt.task.run(tasks);
});
I have a couple grunt tasks and I am trying to share global variables across those tasks and I am running into issues.
I have written a some custom tasks which set the proper output path depending on the build type. This seems to be setting things correctly.
// Set Mode (local or build)
grunt.registerTask("setBuildType", "Set the build type. Either build or local", function (val) {
// grunt.log.writeln(val + " :setBuildType val");
global.buildType = val;
});
// SetOutput location
grunt.registerTask("setOutput", "Set the output folder for the build.", function () {
if (global.buildType === "tfs") {
global.outputPath = MACHINE_PATH;
}
if (global.buildType === "local") {
global.outputPath = LOCAL_PATH;
}
if (global.buildType === "release") {
global.outputPath = RELEASE_PATH;
}
if (grunt.option("target")) {
global.outputPath = grunt.option("target");
}
grunt.log.writeln("Output folder: " + global.outputPath);
});
grunt.registerTask("globalReadout", function () {
grunt.log.writeln(global.outputPath);
});
So, I'm trying to then reference global.outputPath in a subsequent task, and running into errors.
If I call grunt test from the command line, it outputs the correct path no problem.
However, if I have a task like this:
clean: {
release: {
src: global.outputPath
}
}
It throws the following error:
Warning: Cannot call method 'indexOf' of undefined Use --force to continue.
Also, my constants in the setOutput task are set at the top of my Gruntfile.js
Any thoughts? Am I doing something wrong here?
So, I was on the right path. The issue is that the module exports before those global variables get set, so they are all undefined in subsequent tasks defined within the initConfig() task.
The solution I came up with, although, there may be better, is to overwrite a grunt.option value.
I have an optional option for my task --target
working solution looks like this:
grunt.registerTask("setOutput", "Set the output folder for the build.", function () {
if (global.buildType === "tfs") {
global.outputPath = MACHINE_PATH;
}
if (global.buildType === "local") {
global.outputPath = LOCAL_PATH;
}
if (global.buildType === "release") {
global.outputPath = RELEASE_PATH;
}
if (grunt.option("target")) {
global.outputPath = grunt.option("target");
}
grunt.option("target", global.outputPath);
grunt.log.writeln("Output path: " + grunt.option("target"));
});
And the task defined in initConfig() looked like this:
clean: {
build: {
src: ["<%= grunt.option(\"target\") %>"]
}
}
Feel free to chime in if you have a better solution. Otherwise, perhaps this may help someone else.
I have a way to do this that allows you to specify the output path using values like --dev. So far it's working very well, I quite like it. Thought I'd share it, as someone else may like it, too.
# Enum for target switching behavior
TARGETS =
dev: 'dev'
dist: 'dist'
# Configurable paths and globs
buildConfig =
dist: "dist"
dev: '.devServer'
timestamp: grunt.template.today('mm-dd_HHMM')
grunt.initConfig
cfg: buildConfig
cssmin:
crunch:
options: report: 'min'
files: "<%= grunt.option('target') %>/all-min.css": "/**/*.css"
# Set the output path for built files.
# Most tasks will key off this so it is a prerequisite
setPath = ->
if grunt.option 'dev'
grunt.option 'target', buildConfig.dev
else if grunt.option 'dist'
grunt.option 'target', "#{buildConfig.dist}/#{buildConfig.timestamp}"
else # Default path
grunt.option 'target', buildConfig.dev
grunt.log.writeln "Output path set to: `#{grunt.option 'target'}`"
grunt.log.writeln "Possible targets:"
grunt.log.writeln target for target of TARGETS
setPath()
With this setup, you can run commands like:
grunt cssmin --dist #sent to dist target
grunt cssmin --dev #sent to dev target
grunt cssmin --dev #sent to default target (dev)
This is an older question, I just thought to throw in my 5 cents.
If you need config variable to be accessible from any task, just define it in your main (the one that you'll always load) config file like this:
module.exports = function(grunt)
{
//
// Common project configuration
//
var config =
{
pkg: grunt.file.readJSON('package.json'),
options: // for 'project'
{
dist:
{
outputPath: '<%= process.cwd() %>/lib',
},
dev:
{
outputPath: '<%= process.cwd() %>/build',
},
},
}
grunt.config.merge( config )
}
Then you can simply access value like this:
in config file(s)
...
my_thingie:
[
ends_up_here: '<%= options.dev.outputPath %>/bundle',
],
...
in tasks
// as raw value
grunt.config.data.options.dist.outputPath
// after (eventual) templates have been processed
grunt.config('options.dist.outputPath')
I used key options here just to be in line with convention, but you can use anything as long as you remember not to register a task named 'options' or whatever you used for the key :)