Grunt: How do I run seperate processes for CSS and JS - gruntjs

As you can see I have tasks for CSS ['sass:main'] and for JS ['jshint:main', 'concat:main', 'uglify:main'], but I want to do separate tasks for separate files (JS and CSS) and listen for changes (watch). Can someone point me in the correct direction, I'm not really sure what I should be searching for. Is this something that watch can handle, or is there another plugin? I'm a little new to grunt so still trying to figure out how to use it. Thanks
GruntFile.js:
module.exports = function(grunt) {
var config = {
pkg: grunt.file.readJSON('package.json'),
jshint: {
options: {
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
},
main: {
src: [
'assets/templates/main/js/crm/*.js',
]
}
},
concat: {
options: {
separator: '\n\n'
},
main: {
src: [
'assets/templates/main/js/crm/*.js',
],
dest: 'assets/templates/main/js/crm.min.js'
}
},
sass: {
options: {
style: 'compressed'
},
main: {
files: {
'assets/templates/main/css/main.min.css': 'assets/templates/main/sass/main.scss',
}
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
main: {
src: 'assets/templates/main/js/crm.min.js',
dest: 'assets/templates/main/js/crm.min.js'
}
},
watch: {
mainjs: {
files: ['assets/templates/main/js/crm/*.js'],
tasks: ['jshint:main', 'concat:main', 'uglify:main'],
},
mainsass: {
files: ['assets/templates/main/sass/*.scss''],
tasks: ['sass:main'],
}
},
concurrent: {
maincss: ['sass:main'],
mainjs: ['jshint:main', 'concat:main', 'uglify:main']
}
};
grunt.initConfig(config);
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-concurrent');
grunt.registerTask('main', ['jshint:main', 'concat:main', 'uglify:main', 'sass:main']);
grunt.registerTask('main-watch', ['jshint:main', 'concat:main', 'uglify:main', 'sass:main', 'concurrent:mainsass']);
};
When I try run tasks:
$ grunt main-watch
Loading "Gruntfile.js" tasks...ERROR
SyntaxError: Invalid or unexpected token
Warning: Task "main-watch" not found. Use --force to continue.
Aborted due to warnings.

It sounds like you want to perform two concurrent watch tasks. You can do that using a configuration like this:
...
concurrent: {
options: { logConcurrentOutput: true },
watch: ['watch:mainjs', 'watch:mainsass']
}
};
...
grunt.registerTask('main-watch', ['concurrent:watch']);
Note that logConcurrentOutput defaults to false, so if you want to see output logged to the console, you need to set it to true.

Related

Grunt: convert sass to css after file changes (watch)

so this is my gruntfile.js:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
folders: {
'dist': 'dist',
'app': 'app'
},
// configure jshint to validate js files -----------------------------------
jshint: {
options: {
reporter: require('jshint-stylish'),
ignores: [
'Grunfile.js',
'app/assets/lib/**/*'
]
},
all: [
'app/**/**/*.js',
]
},
// configure uglify to minify js files -------------------------------------
uglify: {
options: {
banner: '/*\n <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> \n*/\n'
},
build: {
files: {
'dist/js/app.min.js': 'app/js/main.js'
}
}
},
// compile sass stylesheets to css -----------------------------------------
sass: {
dist: {
options: {
style: 'expanded'
},
files: {
'app/assets/css/main.css': 'app/assets/sass/main.sass',
'app/assets/css/variables.css': 'app/assets/sass/variables.sass',
'app/assets/css/typography.css': 'app/assets/sass/typography.sass',
'app/assets/css/reset.css': 'app/assets/sass/reset.sass'
}
}
},
// starting an express server ----------------------------------------------
express: {
dev: {
options: {
port: 9000,
hostname: '0.0.0.0',
bases: ['app'],
livereload: true
}
}
},
// configure watch to auto update ------------------------------------------
watch: {
sass: {
files: ['**/*.{scss,sass}'],
tasks: ['sass'],
options: {
livereload: true
}
},
reload: {
options: {
livereload: true
},
files: ['app/**/*'],
},
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-express');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['jshint', 'uglify', 'sass', 'express', 'watch:reload']);
};
My problem:
When I start grunt, it checks for my sass files and automatically convert it to css files. Check. But if i start grunt and then edit a sass file, it recognizes the changes but dont convert the sass to css after it again.
Does anybody see the mistake?
Cheers!
Found the "problem": Had to register the task 'watch' instead of 'watch:reload'!
Cheers

Grunt Watch - Verifiying property

I'm attempting to seperate my grunt file so I can process two separate chunks of code, everything seems to work apart from the watch task.
I get the following error which loops out until it exceeds the call stack
Waiting...Verifying property watch.app.files exists in config...ERROR
>> Unable to process task.
Warning: Required config property "watch.app.files" missing.
It seems it doesn't like my watch task being split into two. I've look around and it doesn't seem to be an issue for other people.
My gruntfile looks like this:
module.exports = function(grunt) {
// 1. All configuration goes here
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
app: {
src: [
'themes/site_themes/app/scripts/build/libs/!(html5shiv|respond).js',
'themes/site_themes/app/scripts/build/modules/*.js'
],
dest: 'themes/site_themes/app/scripts/production/app.min.js'
},
marketing: {
src: [
'themes/site_themes/marketing/scripts/build/libs/!(html5shiv|respond).js',
'themes/site_themes/marketing/scripts/build/modules/*.js'
],
dest: 'themes/site_themes/marketing/scripts/production/app.min.js'
}
},
uglify: {
app: {
files: {
'themes/site_themes/app/scripts/production/app.min.js': ['themes/site_themes/app/scripts/production/app.min.js'],
'themes/site_themes/app/scripts/production/html5shiv.min.js': ['themes/site_themes/app/scripts/build/libs/html5shiv.js'],
'themes/site_themes/app/scripts/production/respond.min.js': ['themes/site_themes/app/scripts/build/libs/respond.js'],
}
},
marketing: {
files: {
'themes/site_themes/marketing/scripts/production/app.min.js': ['themes/site_themes/marketing/scripts/production/app.min.js'],
'themes/site_themes/marketing/scripts/production/html5shiv.min.js': ['themes/site_themes/marketing/scripts/build/libs/html5shiv.js'],
'themes/site_themes/marketing/scripts/production/respond.min.js': ['themes/site_themes/marketing/scripts/build/libs/respond.js'],
}
}
},
jshint: {
app: {
all: ['themes/site_themes/app/scripts/build/modules/!(analytics).js', 'themes/site_themes/app/scripts/build/app.js'],
},
marketing: {
all: ['themes/site_themes/marketing/scripts/build/modules/!(analytics).js', 'themes/site_themes/marketing/scripts/build/app.js'],
}
},
sass: {
app: {
options: {
style: 'compressed'
},
files: {
'themes/site_themes/app/styles/production/style.min.css':'themes/site_themes/app/styles/build/style.scss'
}
},
marketing: {
options: {
style: 'compressed'
},
files: {
'themes/site_themes/marketing/styles/production/style.min.css':'themes/site_themes/marketing/styles/build/style.scss'
}
}
},
autoprefixer: {
options: {
browsers: ['last 2 versions', 'ie >= 8']
},
app: {
no_dest: {
src: 'themes/site_themes/app/styles/production/style.min.css',
}
},
marketing: {
no_dest: {
src: 'themes/site_themes/marketing/styles/production/style.min.css',
}
}
},
watch: {
app: {
jshint: {
files: ['themes/site_themes/app/scripts/build/modules/!(analytics).js', 'themes/site_themes/app/scripts/build/app.js'],
tasks: 'jshint:app'
},
scripts: {
files: ['themes/site_themes/app/scripts/build/*/*.js'],
tasks: ['concat:app', 'uglify:app'],
options: {
spawn: false,
},
},
css: {
files: ['themes/site_themes/app/styles/build/*.scss', 'themes/site_themes/app/styles/build/inuit/*/*.scss', 'themes/site_themes/app/styles/build/theme/*/*.scss'],
tasks: ['sass:app', 'autoprefixer:app'],
options: {
livereload: true,
spawn: false,
}
}
},
marketing: {
jshint: {
files: ['themes/site_themes/marketing/scripts/build/modules/!(analytics).js', 'themes/site_themes/marketing/scripts/build/app.js'],
tasks: 'jshint:marketing'
},
scripts: {
files: ['themes/site_themes/marketing/scripts/build/*/*.js'],
tasks: ['concat:marketing', 'uglify:marketing'],
options: {
spawn: false,
},
},
css: {
files: ['themes/site_themes/marketing/styles/build/*.scss', 'themes/site_themes/marketing/styles/build/inuit/*/*.scss', 'themes/site_themes/marketing/styles/build/theme/*/*.scss'],
tasks: ['sass:marketing', 'autoprefixer:marketing'],
options: {
livereload: true,
spawn: false,
}
}
}
}
});
// 3. Where we tell Grunt we plan to use this plug-in.
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-autoprefixer');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
// 4. Where we tell Grunt what to do when we type "grunt" into the terminal.
grunt.registerTask('default', ['concat:app', 'uglify:app', 'jshint:app', 'sass:app', 'autoprefixer:app', 'watch:app']);
grunt.registerTask('marketing', ['concat:marketing', 'uglify:marketing', 'jshint:marketing', 'sass:marketing', 'autoprefixer:marketing', 'watch:marketing']);
};
Just found this. Looks like nested targets aren't supported by watch.
I'll try find another way to to this and post if I do.

Grunt concat not working during watch task

If I run 'grunt concat' it compiles my JS files as per the Grunt file (line 82). However, it doesn't concatenate when I'm doing 'grunt watch' (line 107).
From what I can see, my file is ok but I'm fairly new to grunt so would love to see if you guys can see an issue.
Here is my full Grunt file...
// All scripts to be included in project go here
var _SCRIPTS = [
'js/prefixfree.js',
'js/jquery-1-10-2.js',
'js/ie-detect.js',
'js/application.js'
];
var _PORT = 7777;
module.exports = function(grunt) {
// load all grunt tasks
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
connect: {
server: {
options: {
port: _PORT,
base: 'prototype'
}
}
},
htmlvalidation: {
options: {
},
files: {
src: ['prototype/*.php']
},
},
jshint: {
beforeconcat: _SCRIPTS,
afterconcat: ['js/main.js']
},
csslint: {
check: {
src: ['css/*.css']
},
strict: {
options: {
import: 2
},
src: ['css/*.css']
},
lax: {
options: {
import: false
},
src: ['css/*.css']
}
},
compass: {
dev: {
options: {
config: 'config.rb',
force: false
}
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'js/main.js': ['js/main.js']
}
}
},
concat: {
options: {
stripBanners: true,
banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> */',
},
dist: {
src: _SCRIPTS,
dest: 'js/main.js',
nonull: true
},
},
cssmin: {
add_banner: {
options: {
banner: '/* <%= pkg.name %> - v<%= pkg.version %> - ' +
'<%= grunt.template.today("yyyy-mm-dd") %> */',
},
files: {
'css/style.css': ['css/style.css']
}
}
},
watch: {
concat: {
files: ['js/**/*.js', '!js/main.js'],
tasks: 'concat',
options: {
spawn: false,
},
},
sass: {
files: ['sass/**/*.scss'],
tasks: ['compass:dev'],
options: {
spawn: false,
},
},
/* watch and see if our javascript files change, or new packages are installed */
/* watch our files for change, reload */
livereload: {
files: ['*.html', 'css/*.css', 'img/*', 'js/*.js'],
options: {
livereload: true
}
},
}
});
// Default task (Watch)
grunt.registerTask('default', ['watch']);
// grunt.registerTask('default', [ 'preprocess:dev', 'watch']);
// Watch with localhost (For Static Templates)
grunt.registerTask('watch_with_server', [ 'connect:server', 'watch']);
// TESTING
// Run all tests
grunt.registerTask('allTests', [ 'jshint:beforeconcat', 'concat', 'jshint:afterconcat', 'cssLint', 'htmlvalidation']);
// JS Testing
grunt.registerTask('jsHint', ['jshint:beforeconcat', 'concat', 'jshint:afterconcat']);
// CSS Testing csslint
grunt.registerTask('cssLint', ['csslint:check']);
grunt.registerTask('cssLintLax', ['csslint:lax']);
grunt.registerTask('cssLintStrict', ['csslint:strict']);
// HTML Vaidation
grunt.registerTask('htmlTest', [ 'htmlvalidation']);
grunt.registerTask('printenv', function () { console.log(process.env); });
// Concat and uglify js and minify css for release
grunt.registerTask('release', [ 'concat:dist', 'uglify', 'cssmin']);
};
Many thanks
I managed to get it working by moving 'livereload' above 'concat' in the watch task. No idea why this would make a difference but it's working!
If anyone has any insight on why this would affect it I'd love to know.
add this and see:
livereload: {
files: ['*.html', 'css/*.css', 'img/*', 'js/*.js'],
options: {
livereload: true
}
},
options: {
livereload: true
},
files: '<%= options.watch.files %>',//might have to change this line
tasks: ['default', 'notify:watch']

How to ignore one file in a grunt-watch task?

'use strict';
module.exports = function(grunt) {
// Project Configuration
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
watch: {
jade: {
files: ['app/views/**'],
options: {
livereload: true,
},
},
js: {
files: ['!public/build.js', 'gruntfile.js', 'server.js', 'app/**/*.js', 'public/js/**', 'test/**/*.js'],
tasks: ['uglify', 'jshint'],
options: {
livereload: true,
},
},
html: {
files: ['public/views/**'],
options: {
livereload: true,
},
},
css: {
files: ['public/css/**'],
options: {
livereload: true
}
}
},
jshint: {
all: {
src: ['!public/build.js', 'gruntfile.js', 'server.js', 'app/**/*.js', 'public/js/**', 'test/**/*.js'],
options: {
jshintrc: true
}
}
},
uglify: {
options: {
mangle: false
},
dist: {
files: {
'public/build.js': ['public/js/**/*.js']
}
}
},
nodemon: {
dev: {
options: {
file: 'server.js',
args: [],
ignoredFiles: ['public/**'],
watchedExtensions: ['js'],
nodeArgs: ['--debug'],
delayTime: 1,
env: {
PORT: 3000
},
cwd: __dirname
}
}
},
concurrent: {
tasks: ['nodemon', 'watch', 'uglify'],
options: {
logConcurrentOutput: true
}
},
mochaTest: {
options: {
reporter: 'spec',
require: 'server.js'
},
src: ['test/mocha/**/*.js']
},
env: {
test: {
NODE_ENV: 'test'
}
},
karma: {
unit: {
configFile: 'test/karma/karma.conf.js'
}
}
});
//Load NPM tasks
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-mocha-test');
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-nodemon');
grunt.loadNpmTasks('grunt-concurrent');
grunt.loadNpmTasks('grunt-env');
//Making grunt default to force in order not to break the project.
grunt.option('force', true);
//Default task(s).
grunt.registerTask('default', ['jshint', 'concurrent']);
//Test task.
grunt.registerTask('test', ['env:test', 'mochaTest', 'karma:unit']);
};
I'm trying to exclude the public/build.js file, but it doesn't seem to be working. What am I doing wrong?
Edit:
Why do you need to exclude it from your watch? I do not see any glob pattern in your watch:js task that would look for changed in that file to begin with.
Original Answer:
Have you tried moving '!public/build.js' as the last include in your watch task?
The part of the documentation sited:
"Patterns are processed in-order, with !-prefixed matches excluding matched files from the result set"
Makes me think that the excluded file at the beginning gets added back in with the 'public/js/**' pattern.
I would try changing your js watch task to this.
js: {
files: ['gruntfile.js', 'server.js', 'app/**/*.js', 'public/js/**', 'test/**/*.js', '!public/build.js'],
tasks: ['uglify', 'jshint'],
options: {
livereload: true,
},
},
Add the ! to the beginning of the file name. !public/build.js
You could also add a jshintignore file with all the files you want to ignore inside that.

Using Grunt to set environment based variables

I'm looking at grunt-string-replace to accomplish changing variables in my files to an environment specific variable. However, when I try to run the grunt script below I get this warning: "Task min:dev not found". But in this code I have that defined:
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
min: {
dev: {
"string-replace": {
dist: {
files: {
"/newrelic.js": "/newrelic.js"
},
options: {
replacements: [
{
pattern: /$APPNAME/ig,
replacement: "services"
},
{
pattern: /$ENV/ig,
replacement: "nonprod"
}
]
}
}
}
},
prod: {
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-string-replace');
// Default task(s).
grunt.registerTask('default', ['min:dev']);
grunt.registerTask('prod', ['min:prod']);
};
Am I missing something?
Have you read the manual? The getting started guide states that the tasks expect their configuration to be specified in a property of the same name. You have no 'min' task defined, thus it's erroring.
The top-level keys of the config object need to match the task names, and then you define 'targets' (eg, 'dev' or 'prod') within the task config.
So, here's your Gruntfile re-worked. I have not tested this so I can't promise it will work, but it will certainly lead you in the right direction:
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
"string-replace": {
dev: {
files: {
"/newrelic.js": "/newrelic.js"
},
options: {
replacements: [{
pattern: /$APPNAME/ig,
replacement: "services"
}, {
pattern: /$ENV/ig,
replacement: "nonprod"
}]
}
}
},
uglify: {
prod: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-string-replace');
// Default task(s).
grunt.registerTask('default', ['string-replace:dev']);
grunt.registerTask('prod', ['uglify:prod']);
};
think your trying to do this, this fragment will put the environment variable SOME_ENV, into the grunt config, the replace is simply giving you a easy way to do ${SOME_ENV} placeholders in files for replacement
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg : grunt.file.readJSON('package.json'),
SOME_ENV: process.env.SOME_ENV,
copy: {
files: [
{expand: true, cwd: "src/", src: ["*.*", "**/*.*"], dest: "./build",}
],
options: {
// Replace ${MEH} in files with grunt.config.get("MEH"),
// eg ${SOME_ENV}
// == grunt.config.get("SOME_ENV")
// == process.env.SOME_ENV == systems SOME_ENV environment var
process: function (content, srcpath) {
return content.replace(
/\$\{([a-zA-Z.]+)\}/g,
function replacer(match, p1, offset, string){
var value = grunt.config.get(p1);
grunt.log.write(" in file '" + srcpath + "'\n replacing '" + match + "'\n with '" + value + "'\n");
return value;
});
}
},
}
});
// Production Build Tools
grunt.loadNpmTasks('grunt-contrib-copy');
// Default Production Build task(s).
grunt.registerTask('default', ['copy']);
};

Resources