Just installed latest Grunt on Ubuntu 12.04. Here is my gruntfile:
module.exports = function(grunt){
//project configuration
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
slides : {
src : ['src/top.html', 'src/bottom.html'],
dest : ['build/index.html']
}
}
});
//enable plugins
grunt.loadNpmTasks('grunt-contrib');
grunt.registerTask('default', ['concat:slides']);
}
This creates the build/ directory fine, but gives me the output of:
Running "concat:slides" (concat) task Warning: Unable to write
"build/index.html" file (Error code: undefined). Use --force to
continue.
I tried running chmod 777 on the directory, as I thought it might have something to do with permissions, but that didn't seem to change anything.
How can I make it so Grunt will write to build/index.html?
Figured it out:
//Does not work
dest : ['build/index.html']
Works as a string, but not an array:
//Works
dest : 'build/index.html'
I changed tasks/concat.js to accept arrays for dest:
// Write the destination file.
// If f.dest is an array take the first element
var dest = ([].concat(f.dest))[0]
grunt.file.write(dest, src);
but later I decided to use the files form instead of src/dest:
files: { 'dest.js': ['a.js', 'b.js'] }
Related
1. Summary
I can't set grunt-clean-console plugin, that it works for all my .html files.
2. Details
grunt-clean-console check browser console errors for .html files.
I want to check browser console errors for all .html files of my site. In official descripition I read, how plugin works for specific values of url key. I have many pages in my site; I don't want add each .html file separately. But I can't find, how I can use patterns.
I find, that I can use patterns for built-in Grunt cwd, src, dest keys. But how I can use glob (or another) patterns for custom keys as url of this plugin?
3. Data
Gruntfile.coffee:
module.exports = (grunt) ->
grunt.loadNpmTasks 'grunt-clean-console'
grunt.initConfig
'clean-console':
all:
options:
url: 'output/index.html'
return
example project configuration:
output
│ 404.html
│ index.html
│
├───KiraFirstFolder
│ KiraFirstfile.html
│
└───KiraSecondFolder
KiraSecondFile.html
If I set specific values for url key without patterns as in example above, grunt-clean-console successfully works:
phantomjs: opening page output/index.html
phantomjs: Checking errors after sleeping for 5000ms
ok output/index.html
phantomjs process exited with code 0
Done.
3.1. Steps to reproduce
I run in console:
grunt clean-console --verbose
4. Not helped
4.1. Globbing
Official documentation
Gruntfile.coffee:
module.exports = (grunt) ->
grunt.loadNpmTasks 'grunt-clean-console'
grunt.initConfig
'clean-console':
all:
options:
url: 'output/**/*.html'
return
output:
phantomjs: opening page http://output/**/*.html
phantomjs: Unable to load resource (#1URL:http://output/**/*.html)
phantomjs: phantomjs://code/runner.js:30 in onResourceError
Error code: 3. Description: Host output not found
phantomjs://code/runner.js:31 in onResourceError
phantomjs: loading page http://output/**/*.html status fail
phantomjs://code/runner.js:50
phantomjs process exited with code 1
url output/**/*.html has 1 error(s)
>> one of the urls failed clean-console check
Warning: Task "clean-console:all" failed. Use --force to continue.
Aborted due to warnings.
4.2. Building the object dinamically
Official documentation
Gruntfile.coffee (example):
module.exports = (grunt) ->
grunt.loadNpmTasks 'grunt-clean-console'
grunt.initConfig
'clean-console':
all:
options:
url:
files: [
expand: true
cwd: "output/"
src: ['**/*.html']
dest: "output/"
]
return
output:
File: [no files]
Options: urls=[], timeout=5, url=["output/**/*.html"]
Fatal error: missing url
4.3. Templates
Official documentation
Gruntfile.coffee:
module.exports = (grunt) ->
grunt.loadNpmTasks 'grunt-clean-console'
grunt.initConfig
'clean-console':
all:
options:
url: '<%= kiratemplate %>'
kiratemplate: ['output/**/*.html'],
return
output:
phantomjs: opening page http://output/**/*.html
phantomjs: Unable to load resource (#1URL:http://output/**/*.html)
phantomjs: phantomjs://code/runner.js:30 in onResourceError
Error code: 3. Description: Host output not found
phantomjs://code/runner.js:31 in onResourceError
loading page http://output/**/*.html status fail
phantomjs://code/runner.js:50
phantomjs process exited with code 1
url output/**/*.html has 1 error(s)
>> one of the urls failed clean-console check
Warning: Task "clean-console:all" failed. Use --force to continue.
Aborted due to warnings.
Create a function before the grunt.initConfig part that utilizes grunt.file.expand. For instance:
Gruntfile.js
module.exports = function(grunt) {
grunt.loadNpmTasks 'grunt-clean-console'
// Add this function...
function getFiles() { return grunt.file.expand('output/**/*.html'); }
grunt.initConfig({
'clean-console': {
all: {
options: {
url: getFiles() // <-- invoke the function here.
}
}
}
// ...
});
// ...
}
Notes:
The getFiles function returns an array of file paths for all .html files matching the given glob pattern, i.e. 'output/**/*.html'.
The value of the options.url property is set to getFiles() to invoke the function.
The source config example below processes files from src dir. There is src2 dir which also should be processed with the same tasks and putted to build2. What changes required in config.
module.exports = function (grunt) {
var saveLicense = require('uglify-save-license');
grunt.initConfig({
clean : {
build : {
src : ['build']
},
},
copy : {
files : {
cwd : 'src',
src : '**/*',
dest : 'build',
expand : true
}
},
...
Both grunt-contrib-copy and grunt-contrib-clean, like many other grunt plugins, allow multiple Targets to be specified in each Task.
For your scenario you can simply configure two Targets in the copy task (one Target to copy the src folder and another Target to copy the src2 folder).
You can also configure two Targets in the clean task (one Target to clean the build folder and another Target to clean the build2 folder).
Gruntfile.js
Your Gruntfile.js can be configured as ass follows:
module.exports = function(grunt) {
var saveLicense = require('uglify-save-license');
grunt.initConfig({
// The 'clean' task now includes two targets.
// named 'build1' and 'build2'
clean: {
build1: {
src: ['build']
},
build2: {
src: ['build2']
}
},
// The 'copy' task now includes two targets.
// named 'src1' and 'src2'
copy: {
src1: {
cwd: 'src',
src: '**/*',
dest: 'build',
expand: true
},
src2: {
cwd: 'src2',
src: '**/*',
dest: 'build2',
expand: true
}
}
// ...
});
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-clean');
// Registering the Targets in the Tasks....
grunt.registerTask('copySrc1', ['copy:src1']);
grunt.registerTask('cleanBuild1', ['clean:build1']);
grunt.registerTask('copySrc2', ['copy:src2']);
grunt.registerTask('cleanBuild2', ['clean:build2']);
grunt.registerTask('copyBoth', ['copy']);
grunt.registerTask('cleanBoth', ['clean']);
};
Running the Grunt Tasks
You'll notice that there are six calls to the .registerTask(...) function at the end of the snippet. Namely; copySrc1, cleanBuild1, copySrc2, cleanBuild2, copyBoth, and cleanBoth.
They allow you to run the following commands via your command line:
$ grunt copySrc1
(This will copy the src folder to the build folder)
$ grunt cleanBuild1
(This will clean the build folder)
$ grunt copySrc1
(This will copy the src2 folder to the build2 folder)
$ grunt cleanBuild2
(This will clean the build2 folder)
$ grunt copyBoth
(This will copy the src folder to the build folder and copy the src2 folder to the build2 folder)
$ grunt cleanBoth
(This will clean both the build and build2 folders)
Notes
You probably only need to keep the two .registerTask(...) functions as follows:
grunt.registerTask('copyBoth', ['copy']);
grunt.registerTask('cleanBoth', ['clean']);
However, I included the other four .registerTask(...) functions as they demonstrate how you can call a single Target inside a Task using the semicolon notation (:). For example:
grunt.registerTask('copySrc1', ['copy:src1']);
In the above snippet the ['copy:src1'] part simply runs only the Target named src1 inside the copy Task.
Whereas:
grunt.registerTask('copyBoth', ['copy']);
... does not reference any Targets in the copy task, (i.e. no semicolon notation is used), therefore all Targets will be run.
To further understand Tasks, Targets, you can read my answer to this post.
Hope that helps!
I am learning Grunt and want to use load-grunt-config to start with. Here is my code :
Gruntfile.js
module.exports = function (grunt){
var path = require('path');
require('load-grunt-config')(grunt, {
configPath: path.join(process.cwd(), 'grunt-tasks'),
});
grunt.registerTask('install',['test']);
};
I have grunt-tasks folder in root directory of my project along with Gruntfile.js and in that I have included test.js
test.js
module.exports = {
test :{
local : {
}
}
};
Now when I say grunt install from command line and using CWD as my project's root directory I get following error :
Warning: Task "test" not found. Use --force to continue.
and when I include following segment in Gruntfile.js
loadGruntTasks : {
pattern : '*'
}
I get following error :
>> Local Npm module "load-grunt-config" not found. Is it installed?
Warning: Task "test" not found. Use --force to continue.
May I know what I don't understand here?
I'm trying to use grunt in my ASP project, but for whatever reasons have get a stupid warning-message from Visual Studio.
How you can see down, the bower-task has been executed, but haven't uglify- and less-tasks. Gruntfile.js and folder "wwwroot" are in the same folder. What's wrong ?
This is console output in Visual Studio 2015:
> cmd.exe /c grunt -b "c:\T\BW\src\BW" --gruntfile "c:\T\BW\src\BW\gruntfile.js" bower
Running "bower:install" (bower) task
>> Installed bower packages
>> Copied packages to c:\T\BW\src\BW\wwwroot\lib
Done, without errors.
Process terminated with code 0.
> cmd.exe /c grunt -b "c:\T\BW\src\BW" --gruntfile "c:\T\BW\src\BW\gruntfile.js" uglify_default
Running "uglify:uglify_target" (uglify) task
Process terminated with code 0.
>> Destination wwwroot/lib/angular/angular.js not written because src files were empty.
>> No files created.
Done, without errors.
> cmd.exe /c grunt -b "c:\T\BW\src\BW" --gruntfile "c:\T\BW\src\BW\gruntfile.js" less
Running "less:dev" (less) task
>> Destination wwwroot/css/site.css not written because no source files were found.
Done, without errors.
Process terminated with code 0.
This is my gruntfile.js:
grunt.initConfig({
bower: {
install: {
options: {
targetDir: "wwwroot/lib",
layout: "byComponent",
cleanTargetDir: false
}
}
},
uglify: {
uglify_target: {
files: {
"wwwroot/lib/angular/angular.js":["src/angular.min.js"]
}
}
},
less: {
dev: {
files: {
"wwwroot/css/site.css": ["less/site.less"]
},
options: {
sourceMap: true,
}
}
},
});
grunt.registerTask("default", ["bower:install"]);
grunt.loadNpmTasks("grunt-bower-task");
grunt.loadNpmTasks("grunt-contrib-uglify");
grunt.loadNpmTasks("grunt-contrib-less");
Are you sure "wwwroot/lib/angular/angular.js":["src/angular.min.js"] is correct in your uglify step? Uglify is for minifying files, so it would seem as if you have the destination and source parameters reversed. You are also getting the message Destination wwwroot/lib/angular/angular.js not written because src files were empty. which would seem to indicate that as well.
The less step seems correct, but it can't find the less file you are trying to compile. Are you sure that site.less exists in a less folder?
Here is the less portion of the grunt file I just wrote, and it seems to work well, although getting the sourcemap to actually generate is still an issue. This is pretty much an exact equivalent of VS 2013 with Web Essentials installed. Compiling all less files in the content folder using autoprefix for the browsers we support, and puts the .css back into the content folder. It doesn't compile any less file that starts with _, as those are just includes/variables.
less: {
development: {
options: {
paths: ["content"],
compress: true,
plugins: [
new (require('less-plugin-autoprefix'))({ browsers: ["IE >= 9, firefox > 10, last 2 Chrome versions, last 2 safari versions, last 2 ios versions"], map: { inline: false } })
]
},
files: [{
expand: true,
cwd: 'content',
src: ['*.less', '!_*.less'],
dest: 'content',
ext: '.css'
}]
}
},
using gurunt syncall, I want to dynamically sync every dir(and new dir created in the futrue) in project root except 'node_modules' dir to another dir.
using grunt watchall, I want to watch those dirs, when there are some changes in a dir, I want to automaticlly run the corresponding sync task.
here is my project root's structure:
├── Gruntfile.js
├── addon1
│ └── a.toc
├── adon3
│ └── 3.toc
├── node_modules
│ ├── grunt
│ ├── grunt-contrib-watch
│ └── grunt-sync
└── package.json
the grunt syncall command is ok, here is the result:
➜ testsync grunt syncall
Running "config" task
Running "sync:addon1" (sync) task
Running "sync:adon3" (sync) task
Done, without errors.
but the grunt watchall not ok. can you tell me why watchall tasks not work and how to fix it?
I start the command, and change and save the file '3.toc' in dir 'adon3', then grunt say:
➜ testsync grunt watchall
Running "config" task
Running "watch" task
Waiting...
>> File "adon3/3.toc" changed.
Running "sync:adon3" (sync) task
Verifying property sync.adon3 exists in config...ERROR
>> Unable to process task.
Warning: Required config property "sync.adon3" missing. Use --force to continue.
Aborted due to warnings.
here is my Gruntfile.js:
module.exports = function(grunt) {
grunt.initConfig({});
var destdir = "/Users/morrxy/project/testdest/";
// dynamic config sync and watch's targets for every dir except for node_modules
grunt.registerTask('config', 'config sync and watch', function() {
grunt.file.expand({filter: 'isDirectory'},
['*', '!node_modules']).forEach(function(dir) {
// config sync's targets
var sync = grunt.config.get('sync') || {};
sync[dir] = {
files: [{
cwd: dir,
src: '**',
dest: destdir + dir
}]
};
grunt.config.set('sync', sync);
// config watch's target
var watch = grunt.config.get('watch') || {};
watch[dir] = {
files: dir + '/**/*',
// is next line has problem?
// running 'grunt watchall'
// when I change and save the file '3.toc' in dir 'adon3', terminal say:
// >> File "adon3/3.toc" changed.
// Running "sync:adon3" (sync) task
// Verifying property sync.adon3 exists in config...ERROR
// >> Unable to process task.
// Warning: Required config property "sync.adon3" missing.
// but why 'grunt syncall' can work?
tasks: 'sync:' + dir
};
grunt.config.set('watch', watch);
});
});
grunt.loadNpmTasks('grunt-sync');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('syncall', ['config', 'sync']);
grunt.registerTask('watchall', ['config', 'watch']);
};
Finally, I solved this problem. I add a github repo for this question. If you want test, you can clone it run it locally. https://github.com/morrxy/dynasync
The reason watchall can watch files changes but can't run the corresponding sync task is that when watch find files changed, the config for each sync is finnished running, so the config for that sync target is gone, we didn't save the running config any where. So to solve this problem, we could add the config task into the watch task before the sync task, like this tasks: ['config_sync', 'sync:' + dir].
in new Gruntfile.js, I split the config task to two task, one for config watch, one for config sync, in the watch task, using the config sync task. Here is the new Gruntfile.js
module.exports = function(grunt) {
var destdir = "/media/data/projects/dynasync_dest/";
grunt.loadNpmTasks('grunt-sync');
grunt.loadNpmTasks('grunt-contrib-watch');
// dynamic config sync's targets for every dir except for node_modules
grunt.registerTask('config_sync', 'dynamically config sync', function() {
grunt.file.expand({filter: 'isDirectory'},
['*', '!node_modules']).forEach(function(dir) {
// config this dir's sync target
var sync = grunt.config.get('sync') || {};
sync[dir] = {
files: [{
cwd: dir,
src: '**/*',
dest: destdir + dir
}]
};
grunt.config.set('sync', sync);
});
});
// dynamic config watch's targets for every dir except for node_modules
grunt.registerTask('config_watch', 'dynamically config watch', function() {
grunt.file.expand({filter: 'isDirectory'},
['*', '!node_modules']).forEach(function(dir) {
// config this dir's watch target
var watch = grunt.config.get('watch') || {};
watch[dir] = {
files: dir + '/**/*',
// this line solve the problem
// when find file change, first dynamically config sync and then sync the dir
tasks: ['config_sync', 'sync:' + dir]
};
grunt.config.set('watch', watch);
});
});
grunt.registerTask('syncall', ['config_sync', 'sync']);
grunt.registerTask('watchall', ['config_watch', 'watch']);
grunt.registerTask('default', ['watchall']);
};
This time, when watch find file changes, it can run the corresponding sync task. like this
grunt watchall
Running "config_watch" task
Running "watch" task
Waiting...
>> File "adon3/3.toc" changed.
Running "config_sync" task
Running "sync:adon3" (sync) task
Done, without errors.
Completed in 0.888s at Thu Apr 10 2014 14:01:26 GMT+0800 (CST) - Waiting...