I’m trying to get grunt to concat some .css files from 2 different directories into a single css file
I got it working ok and am trying to write a similar piece of code to concat html files in the order I specify in a simple external .json file rather than editing the Grunfile.js directly each time but it keeps throwing errors
The file locations and permissions are all ok, I can do it with php or concat the css files ok, I’m not great on js syntax os using functions. I only learned to use Grunt in the last few days so am pretty sure have not called the file with the correct code??
Any advice kindly appreciated!
This is my Gruntfile.js exactly
module.exports = function(grunt) {
// 1. All configuration goes here
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
// 2. Configuration for concatinating files goes here
file.readJSON(‘html-files.json’);
},
watch: {
files: ['../../pages/**/*.html’],
tasks: ['concat'],
options : { nospawn : true }
}
});
// 3. Where we tell Grunt we plan to use this plug-in
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
// 4. Where we tell Grunt what to do when we type "grunt" into the terminal.
grunt.registerTask('default', ['concat']);
};
This is the html-files.json file contents exactly
html: {
src: [
'../../pages/**/this-block.html',
'../../pages/**/that-block.html',
],
dest: '../../somepage/content.html',
}
Related
i'm using grunt tasks to watch and concatenate some js files.
my first configuration worked fine:
grunt.initConfig({
watch: {
js: {
files: ['web/**/*.js'],
tasks: ['concat:JS']
}
},
concat: {
JS: {
files: {
"index.js": [
"test-1.js",
"test-2.js",
],
"about_us.js": [
"test-3.js",
"test-4.js",
],
"store.js": [
"test-5.js",
"test-6.js",
]
}
}
}
});
my problem is that with this configuration whenever i change a file it concatenates everything.
i would be able to concatenate only a specific file (based on which file changed).
e.g. if i change test-1.js i would like concatenate only the index.js file (..so the task to run should be 'concat:JS:index.js' )
so i tryed adding a watch event
grunt.event.on('watch', function(action, filepath, target) {
var taskName = "concat:JS"; //here i calculate dinamically the specific task name to be run
grunt.task.run(taskName);
});
but nothing happens.. any idea? thanks
New answer post edit to question
Using the grunt.event.on('watch', ...) listener is the wrong tool for this kind of requirement. Instead you should utilize multiple Targets for both the the watch and concat tasks as shown in the Gruntfile.js excerpt below.
Gruntfile.js
grunt.initConfig({
watch: {
'JS-index': {
files: ['web/js/test-1.js', 'web/js/test-2.js'],
tasks: ['concat:JS-index']
},
'JS-about-us': {
files: ['web/js/test-3.js', 'web/js/test-4.js'],
tasks: ['concat:JS-about-us']
},
'JS-store': {
files: ['web/js/test-5.js', 'web/js/test-6.js'],
tasks: ['concat:JS-store']
}
},
concat: {
'JS-index': {
files: {
'dist/js/index.js': [
"web/js/test-1.js",
"web/js/test-2.js",
]
}
},
'JS-about-us': {
files: {
"dist/js/about_us.js": [
"web/js/test-3.js",
"web/js/test-4.js",
]
}
},
'JS-store': {
files: {
'dist/js/store.js': [
"web/js/test-5.js",
"web/js/test-6.js",
]
}
}
}
});
Notes
Given the configuration shown above, the following will happen after running grunt watch via your CLI:
Changing either test-1.js or test-2.js will concatenate both files to dist/js/index.js.
Changing either test-3.js or test-4.js will concatenate both files to dist/js/about_us.js.
Changing either test-5.js or test-6.js will concatenate both files to dist/js/store.js.
Your paths will need to be configured as necessary
Original answer before edit to question
how do i execute a task with grunt?
Using grunt.task.run is the correct way to programmatically run a task.
However, see this comment in the grunt-contrib-watch github repo. Excerpts from it read:
"Don't use grunt.event.on('watch') to run your tasks."
and...
"The watch event is not intended for running tasks."
The following solution assumes your requirement is to concatenate some .js files when one of them is changed/edited. In which case you need to utilize the tasks property of the watch task to define which task to run (I.e. 'concat:JS')
Gruntfile
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.initConfig({
watch: {
js: {
files: ['path/to/js/**/*.js'], // <-- 1. Glob pattern to .js files
tasks: ['concat:JS'] // <-- 2. Task to run when .js file changes.
}
},
concat: { // <-- 3. Configure your `concat` task as necessary.
JS: {
src: [
'path/to/js/foo.js',
'path/to/js/bar.js',
'path/to/js/quux.js'
],
dest: 'path/to/output/combined.js'
}
}
});
grunt.registerTask('default', ['watch']);
grunt.event.on('watch', function(action, filepath, target) {
console.log("yep, this code is being executed");
// 4. <-- Do not run task from 'on' listener event.
});
}
Notes
In your watch task define the glob pattern to the source .js files to watch for changes.
Specify the task to run when a .js file changes; I.e. 'concat:JS'
Configure your concat task as necessary.
Remove grunt.task.run('concat:JS'); from the grunt.event.on('watch', ...) listener.
Using the gist above and running grunt via your cli creates a new combined.js file when any change is made to a .js file stored in the path/to/js/ directory. (You'll need to configure the paths as per your requirements)
Whilst the grunt.event.on('watch', ...) listener should not be used to run a task, it can be utilized to configure other tasks.
I've been searching around but there seemed to be no situation similar to mine so thought I'd post to ask. I want to run the handlebars task in Gruntfile.js with grunt handlebars to compile a templates.js in my source folder (www) but it doesn't fire up with this error shown:
Warning: Cannot read property 'filter' of undefined Use
Here's my script for the handlebars task in grunt file:
// Create the tasks
grunt.initConfig({
config: config,
handlebars: {
// Compiles the handlebar templates into templates.js
compile: {
options: {
amd: true,
processName: function (filepath) {
var pieces = filepath.split('/');
return pieces[pieces.length - 1].split('.')[0];
}
},
// Specify location of handlebar templates
www: ['<%= config.www %>/html/{,*/}*.handlebars'],
dest: '<%= config.www %>/js/templates.js'
}
}
});
Here's opening script of grunt file and the config object, before grunt.initConfig :
module.exports = (function () {
'use strict';
return function (grunt) {
require('load-grunt-tasks')(grunt); // Several tasks to run using grunt-contrib-xx plugins
// Config object
var config = {
www: 'www', // all source files in one directory
};
.. // grunt.initConfig
};
});
Couldn't figure out what goes wrong here since I don't even define a property/term filter and that's the only error received. Any thoughts would be appreciated.
So I will write what I did to resolve this which might be of use to you.
I simply changed the www property specifically to src, as referred to in the first code block, for the handlebars task in grunt file to fire up. Existing handlebars which are markups are sourced a.k.a. src and to compile the templates.js in your specified destination.
Note: handlebars file itself should end with unspecified file type (.handlebars). Depending on your own set up, noting this will save you lots of time afterwards.
Now run below and you shall see the templates.js found in the dest folder.
grunt handlebars
When i'm concatenating bower_components of angularjs (angular & ui-router) using grunt,
I get a (Error: $injector:modulerr Module Error) Complete error here https://goo.gl/0yz6pm
on the built script.
I DO NOT get this error when i'm using the script source directly from the bower_components
Therefore i think it's an issue with concatenation by grunt.
Below is the grunt script,
grunt.initConfig({
concat:{
options: {
},
dist: {
// the files to concatenate
src: ['client/bower_components/angular/angular.min.js','client/bower_components/angular-ui-router/release/angular-ui-router.min.js'],
// the location of the resulting JS file
dest: 'client/bower_components/../assets/scripts/coreScript.js',
nonull: true
}
}
})
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('build',['concat']);
}
I've also tried using a seperator:';' in options but to no help.
What can i do to make concatenation work here?
This setup looks fine, assuming all those script references are correct and you are referencing coreScript.js on your page. The only issue I can see here is your call to .registerTask. Observe your implementation
grunt.registerTask('build','concat']);
However, grunt is expecting a task array
grunt.registerTask('build', ['concat']); // close array with [
Could it be this task is not successfully running because of this issue? I am also not seeing a call to readJSON. Could you be also missing the following in your gruntfile.js declaration?
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
// ...
}
});
As a part of my Gruntfile.js:
...
concat: {
...
js: {
src: [
'src/**/*.js'
],
dest: 'build/js/main.js',
nonull: true
}
},
...
How can I prevent concat from generating a blank main.js if my src directory contains no scripts? Must I really create a separate task if I know I won't need to build any scripts?
Not exactly a proper solution, but I managed to find a package called grunt-cleanempty which I set to run after concat to delete any generated empty files.
I am currently using Grunt, and as I was trying Gulp, the same problem I encountered first with Grunt occurred to me.
I am trying to process some js files (concat, uglify and minify them), but I don't want all of them to compile into one big file, I want multiple output files, each from the processing of some input files :
scripts =
firstOutput:
outputFilename: 'first.min.js',
inputFiles: ['one.js', 'two.js']
secondOutput:
outputFilename: 'second.min.js',
inputFiles: ['three.js']
thirdOutput:
outputFilename: 'third.min.js',
inputFiles: ['four.js', 'five.js']
The only way I found (for now) to achieve that with Grunt is with multiple watches and multiple uglify tasks (or one uglify task and a listener on watch change to dynamically modify the uglify task src and dest) :
module.exports = (grunt) ->
grunt.loadNpmTasks 'grunt-contrib-watch'
grunt.loadNpmTasks 'grunt-contrib-uglify'
grunt.initConfig
watch:
firstOutput:
files: scripts.firstOutput.inputFiles
tasks: ['uglify:firstOutput']
options :
spawn : false
secondOutput:
files: scripts.secondOutput.inputFiles
tasks: ['uglify:secondOutput']
options :
spawn : false
thirdOutput:
files: scripts.thirdOutput.inputFiles
tasks: ['uglify:thirdOutput']
options :
spawn : false
uglify:
firstOutput:
files: scripts.firstOutput.inputFiles
dest: scripts.firstOutput.outputFilename
secondOutput:
files: scripts.secondOutput.inputFiles
dest: scripts.secondOutput.outputFilename
thirdOutput:
files: scripts.thirdOutput.inputFiles
dest: scripts.thirdOutput.outputFilename
grunt.registerTask 'default', 'watch'
And, as you can imagine, this is just an example, in my case of a big web application, there's a lot more than just three output js files, and I also process a few less files into some css files
My Gruntfile is really huge, and I find it has a lot of duplicate code, is there any way to have this code refactored to have one watch and one uglify task, with an automatically guessed src and dest with some kind of dependency (to know that if the four.js file is modified, it has to process the third output) ?
If you have some way to do it with Gulp I'll take it with great pleasure, as I would like to test it in my usual workflow.
Here's how you can do this with gulp + vanilla javascript:
var _ = require("underscore")
, gulp = require("gulp")
, uglify = require("gulp-uglify")
var scripts = [
{
output: 'first.min.js',
input: ['one.js', 'two.js']
}
, {
output: 'second.min.js',
input: ['three.js']
}
, {
output: 'third.min.js',
input: ['four.js', 'five.js']
}
];
function build(files, dest) {
return gulp.src(files)
.pipe(uglify())
.pipe(gulp.dest(dest));
}
gulp.task("watch", function () {
_.each(scripts, function (script, i) {
gulp.watch(script.input, function () {
build(script.input, script.output);
});
});
});
Even better if you can use globs to match sets of files so you don't have to write out the path for every single input set. Something like input: ["one/**/*.js, "other/**/*.js"]
"I am trying to process some js files (concat, uglify and minify
them), but I don't want all of them to compile into one big file"
Can I ask why? The benefit of one larger file is that you save on HTTP requests, every resource you load will cause some slowdown of your website. May I suggest using proper dependency management with RequireJS? That way the optimiser can walk your dependency graph and output optimised files for you.
http://requirejs.org/
There's a grunt task for this too:
https://github.com/gruntjs/grunt-contrib-requirejs