Getting started on minifying CSS/SCSS with Grunt? - node.js

After inheriting a project, I've found the developer was using SCSS and Grunt to Minify his code. I'm new to both, but noticed I had to install grunt-cli locally. I've done so and need to make some edits to the style sheets, but am still working through how to get the scss to minify after making changes.
The structure of the grunt/scss area is:
_ (root folder)
_/css
_/grunt
_/img
_/inc
_/img
_/js
_/misc
_/sass
Inside _/grunt is:
gruntfile.js
npm-debug.log
package.json
Steveax helped me figure our that I was missing the local grunt setup:
npm install
I've found the SCSS files inside the _/scss folder and am comfortable editing them for the purposes of updating styles; however, after saving one I've noticed that they don't automatically update the minified css in the _/css folder, and am left looking through the files and documentation for a solution. I think an experienced eye will know what command I've missed.
Here is an example of an scss file, _/sass/common.scss, I'd like to be able to save and replace the css equivalent, _/css/common.css
EDIT: Per the help of Robert Levy (below), it seems I just need to run Grunt after making changes.
(x-See edit above) Do I simply run uglify from the _ directory?
uglify /sass/common.scss -o /css/common.css -p 1
Inside of package.json is:
{
"name": "exampletheme.com",
"version": "0.1.0",
"devDependencies": {
"grunt": "~0.4.1",
"grunt-contrib-concat": "^0.3.0",
"grunt-contrib-uglify": "^0.4.0",
"grunt-contrib-imagemin": "^0.5.0",
"grunt-contrib-watch": "^0.6.0",
"grunt-contrib-compass": "^0.7.2",
"grunt-contrib-sass": "^0.7.3"
}
}
Inside of _/grunt/gruntfile.js is:
module.exports = function(grunt) {
// All configuration goes here
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
// Configuration for concatinating files goes here.
dist: {
src: [
'../js/libs/owl.carousel.js',
'../js/libs/jquery.actual.js',
'../js/libs/chosen.jquery.js',
'../js/libs/jquery.parallax.js',
'../js/src/common.js'
],
dest: '../js/pro/global.js',
},
},
uglify: {
build: {
src: '../js/pro/global.js',
dest: '../js/pro/global.min.js',
},
},
imagemin: {
dynamic: {
files: [{
expand: true,
cwd: '../img/src/',
src: ['**/*.{png,jpg,gif}'],
dest: '../img/pro/'
}]
}
},
compass: {
dev: {
options: {
sassDir: '../sass',
cssDir: '../css',
fontsDir: '../fonts',
imagesDir: '../img/',
images: '../img/',
javascriptsDir: '../js/pro',
//environment: 'development',
outputStyle: 'compressed',
relativeAssets: false,
httpPath: '.',
}
},
},
watch: {
scripts: {
files: ['../js/**/**.js'],
tasks: ['concat', 'uglify'],
options: {
spawn: false,
},
},
images: {
files: ['../img/src/**.{png,jpg,gif}'],
tasks: ['imagemin'],
options: {
spawn: false,
}
},
compass: {
files: ['../**/*.{scss,sass}'],
tasks: ['compass:dev'],
}
},
svgstore: {
defaults: {
options: {
prefix : 'icon-',
},
files: {
'../img/svg-defs.svg': ['../img/svg/*.svg']
}
}
},
});
// 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-imagemin');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-svgstore');
// Where we tell Grunt what to do when we type "grunt" into the terminal.
grunt.registerTask('default', ['concat', 'uglify', /*'imagemin',*/ 'compass', 'svgstore', 'watch']);
};

just run grunt and it will invoke the default task which you can see in the last line of your gruntfile.js will in turn run concat, uglify, compass, svgstore, and watch.
compass is the one which is compiling the scss and minifying it.
the watch task is interesting because it tells grunt to continue running, keep an eye on your files, and recompile when things change.

Related

Grunt: Livereload doesn't work

I keep running into this problem everytime I try to use the live reload option of grunt watch.
No matter which port I use, I keep getting the "Fatal error: Port xxxx is already in use by another process."
I listed all ports in use, and choosing a random number that doesn't appear in the list doesn't help: it substitues xxxx with whatever port was last used in the gruntfile.
My gruntfile itself:
module.exports = function (grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
watch: {
options: {
livereload: true
},
js: {
files: ['.dev/**/*.js'],
tasks: ['concat', 'uglify', 'watch'],
},
scss: {
files: ['./dev/scss/*.scss'],
tasks: ['sass', 'concat', 'watch'],
options: {
reload: true,
livereload: false,
}
},
html: {
files: ['./dev/**/*.html', 'watch'],
tasks: ['copy', 'watch'],
},
grunt: {
files: ['Gruntfile.js'],
tasks: ['watch'],
},
},
concat: {
js: {
src: ['dev/js/script1.js', 'dev/js/script2.js'],
dest: 'dev/js/script.js',
},
css: {
src: ['./dev/css/nav.css', './dev/css/anim.css', './dev/css/style.css'],
dest: './dist/css/style.css',
},
},
uglify: {
build: {
src: 'dev/js/script.js',
dest: 'dist/js/script.min.js'
}
},
sass: { // Task
dev: { // Target
files: { // Dictionary of files
'./dev/css/style.css': './dev/scss/style.scss', // 'destination': 'source'
},
tasks: ['copy'],
}
},
copy: {
main: {
expand: true,
cwd: './dev/html',
src: '*.html',
dest: './dist/html/'
},
},
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-sass');
// Default task(s).
grunt.registerTask('default', ['concat', 'uglify', 'sass', 'copy', 'watch']);
};
I'm still new to npm, grunt etc, and have had this issue using Yeoman as well.
Is there some application that might automatically listen to every port (I'm using Webstorm)?
However, when I close it down and use sublime and a terminal, it keeps on saying every random port is already in use
Looks like you're recursively calling watch..
Take this portion of your gruntfile for example:
watch: { // hey grunt, you now know of a task called 'watch'
....
html: {
files: ['./dev/**/*.html', 'watch'], // here are the files to watch
tasks: ['copy', 'watch'], // and oh, hey, when you run watch, make
// sure to run watch again, recursively, forever
}
I think you can see where this is going. Your port is in use because your watch task is already running when it tries to run again. You don't need to register 'watch' as a task on each subsection of watch. Hope that helps :)

How can i get jade template to compile automatically in visual studio on save operation?

Could anyone post some basic steps on how to get *.html files to compile to *.jade files on file change / save operation in Visual Studio?
I have installed nodetools, web essentials. Syntax highlighting seems to work, but creating a .jade file does nothing. I think there is a missing step somewhere.
Do I have to use something like grunt-contrib-jade with a task?
Visual Studio 2015: After fiddling around a lot the answer i have is as follows:
Install node
Install NodeTools for visual studio
Run: npm install jade (install jade)
Run: npm install -g grunt-cli (install grunt)
Run: npm install bower
Create the below package.json file
Package.json : as follows
{
"name": "myapp",
"version": "0.1.0",
"devDependencies": {
"grunt": "~0.4.5",
"grunt-contrib-uglify": "~0.5.0",
"grunt-contrib-jade": "0.15.0",
"grunt-contrib-watch": "0.6.1"
}
}
7) Create the following grunt.js file
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
jade: {
compile: {
options: {
data: {
debug: true,
timestamp: "<%= new Date().getTime() %>"
}
},
files: [{
expand: true,
src: '**/*.jade',
ext : '.html'
}]
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'Scripts/bootstrap.js',
dest: 'Scripts/build/bootstrap.min.js'
}
},
watch: {
jade: {
files: '**/*.jade',
tasks: ['jade:watch'],
options: {
spawn: false
}
}
}
});
grunt.event.on('watch', function (action, filepath) {
if (filepath.indexOf('.jade') === -1) return;
var file = {};
var destfile = filepath.replace('.jade', '.html');
file[destfile] = filepath
grunt.config('jade.watch.files', file);
});
grunt.loadNpmTasks('grunt-contrib-watch');
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-jade');
// Default task(s).
grunt.registerTask('default', ['uglify']);
};
Open Task Explorer and then make sure you add/bind the task "watch" to project open.

grunt error: cannot find module 'load-grunt-tasks'

When I am using grunt command it is showing me following error:
$ grunt
Loading "Gruntfile.js" tasks...ERROR
>> Error: Cannot find module 'load-grunt-tasks'
Warning: Task "default" not found. Use --force to continue.
Aborted due to warnings.
Execution Time (2015-02-07 18:05:42 UTC)
loading tasks 339ms ███████████████████████████████████████████████ 99%
Total 344ms
I already tried - npm install, npm update commands. It would be great if someone can help me with this. Thanks!
Addding Content of Gruntfile.js
'use strict';
var paths = {
js: ['*.js', 'test/**/*.js', '!test/coverage/**', '!bower_components/**', 'packages/**/*.js', '!packages/**/node_modules/**', '!packages/contrib/**/*.js', '!packages/contrib/**/node_modules/**'],
html: ['packages/**/public/**/views/**', 'packages/**/server/views/**'],
css: ['!bower_components/**', 'packages/**/public/**/css/*.css', '!packages/contrib/**/public/**/css/*.css']
};
module.exports = function(grunt) {
if (process.env.NODE_ENV !== 'production') {
require('time-grunt')(grunt);
}
// Project Configuration
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
assets: grunt.file.readJSON('config/assets.json'),
clean: ['bower_components/build'],
watch: {
js: {
files: paths.js,
tasks: ['jshint'],
options: {
livereload: true
}
},
html: {
files: paths.html,
options: {
livereload: true,
interval: 500
}
},
css: {
files: paths.css,
tasks: ['csslint'],
options: {
livereload: true
}
}
},
jshint: {
all: {
src: paths.js,
options: {
jshintrc: true
}
}
},
uglify: {
core: {
options: {
mangle: false
},
files: '<%= assets.core.js %>'
}
},
csslint: {
options: {
csslintrc: '.csslintrc'
},
src: paths.css
},
cssmin: {
core: {
files: '<%= assets.core.css %>'
}
},
nodemon: {
dev: {
script: 'server.js',
options: {
args: [],
ignore: ['node_modules/**'],
ext: 'js,html',
nodeArgs: ['--debug'],
delayTime: 1,
cwd: __dirname
}
}
},
concurrent: {
tasks: ['nodemon', 'watch'],
options: {
logConcurrentOutput: true
}
},
mochaTest: {
options: {
reporter: 'spec',
require: [
'server.js',
function() {
require('meanio/lib/core_modules/module/util').preload(__dirname + '/packages/**/server', 'model');
}
]
},
src: ['packages/**/server/tests/**/*.js']
},
env: {
test: {
NODE_ENV: 'test'
}
},
karma: {
unit: {
configFile: 'karma.conf.js'
}
}
});
//Load NPM tasks
require('load-grunt-tasks')(grunt);
/**
* Default Task
*/
grunt.hook.push('clean', -9999);
grunt.hook.push('concurrent', 9999);
if (process.env.NODE_ENV === 'production') {
grunt.hook.push('cssmin', 100);
grunt.hook.push('uglify', 200);
} else {
grunt.hook.push('jshint', -200);
grunt.hook.push('csslint', 100);
}
//Default task.
grunt.registerTask('default', ['hook']);
//Test task.
grunt.registerTask('test', ['env:test', 'mochaTest', 'karma:unit']);
// For Heroku users only.
// Docs: https://github.com/linnovate/mean/wiki/Deploying-on-Heroku
grunt.registerTask('heroku:production', ['cssmin', 'uglify']);
};
Try running:
$ npm install
After that, if you run it and the error still persists or if there's another one, then you probably have not installed ruby, compass or both :)
I had the same issue, the problem for me was in my package.json where I didn't actually install the NPM package needed and it was not automatically installed as previously thought. Try doing
npm install --save-dev load-grunt-tasks
If that doesn't work can you provide the package.json file as well so we can get a little more information.
I was having the same issue you were having, it seems as though the gruntfile is missing a required initialization step.
By changing this:
require('load-grunt-tasks')(grunt);
/**
* Default Task
*/
grunt.hook.push('clean', -9999);
to this:
require('load-grunt-tasks')(grunt);
grunt.loadNpmTasks('grunt-hook');
/**
* Default Task
*/
grunt.hook.push('clean', -9999);
Adding the grunt.loadNpmTasks call, I'm able to get past that issue. The problem is, now I'm getting
Task "clean" not found. Use --force to continue.
Looking at the rest of the grunt file, i don't see a register task for clean. If I go to the mean.io docs, it looks like the build is failing. Perhaps this is part of why? I think I asked mean-cli for gulp version, that's probably why. I'll delete and take it from the top :)
I think the problem is related to where the npm dependencies are declared and the way Heroku handles them.
In few words, check if the npm packages are as dev dependencies and move them to the dependencies block, as suggested here: https://stackoverflow.com/a/20687098/532912.

grunt-contrib-less creates the app.css file during launch but not during live reload

Basically, at the beginning all is ok but after modifications, CSS are not updated.
First, my package details:
"node" : "0.10.21"
[...]
"grunt": "~0.4.1",
"grunt-contrib-uglify": "~0.2.7",
"grunt-contrib-jshint": "~0.7.1",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-watch": "~0.5.3",
"grunt-contrib-less": "~0.8.1"
Next, my gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ''
},
dist: {
src: [ 'src/js/libs/jquery-2.0.3.min.js',
'src/js/libs/**/*.js',
'src/js/app/**/*.js'],
dest: 'public/js/combo.js'
}
},
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("dd-mm-yyyy") %> */\n'
},
dist: {
files: {
'public/js/combo.min.js': ['public/js/combo.js']
}
}
},
less: {
compile: {
options: {
compress:true
},
files: {
'public/css/application.css': ['src/css/application.less']
}
}
},
jshint: {
files: ['gruntfile.js', 'src/js/**/*.js','src/css/**/*.less','public/index.html'],
options: {
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['default']
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['concat','uglify','less','watch']);
};
Explanations
At first, all works perfectly, all the files are created:
Running "concat:dist" (concat) task
File "public/js/combo.js" created.
Running "uglify:dist" (uglify) task
File "public/js/combo.min.js" created.
Running "less:compile" (less) task
App runnning on http://localhost:3000
File public/css/application.css created.
Running "watch" task
Waiting...
But, if I make a modification in my *.less files:
OK
>> File "src\css\application.less" changed.
Running "concat:dist" (concat) task
Completed in 2.039s at Tue Nov 12 2013 11:44:53 GMT-0500 (Eastern Standard Time) - Waiting...
So, modifying is detected by watch but my CSS file is not updated and I just can't figure why. I tried several contrib-less implementations and it keeps the same way. I guess I'm doing something wrong but what ?
PS: concat and uglify are working very well.

How do I automate the task of compiling front-end frameworks like Twitter Bootstrap in my Node.js project?

How do I automate the task of compiling Twitter Bootstrap in my Node.js project?
I'm editing the LESS files that compile into a custom build of Bootstrap for my Node.js project, so I can't just use the online customizer or the pre-compiled JavaScript/CSS distribution.
How do I use something like Grunt or Bower to automate the process of building and compiling the Twitter Bootstrap front-end framework into my project from source?
Is there a package manager for front-end libraries and frameworks?
I'm using Grunt to compile my LESS. Here are the dependencies which you have to add to your package.json:
"devDependencies": {
"grunt": "0.4.1",
"grunt-contrib-concat": "0.3.0",
"grunt-contrib-watch": "0.4.4",
"assemble-less": "0.4.8"
}
And here is how my Gruntfile.js looks like:
module.exports = function(grunt) {
grunt.initConfig({
less: {
project: {
options: {
paths: ['src/css/less'],
yuicompress: true
},
src: ['src/css/less/index.less'],
dest: 'src/css/styles.css'
}
},
watch: {
css: {
files: ['src/css/less/**/*.less'],
tasks: ['less']
}
}
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('assemble-less');
// grunt.registerTask('default', ['concat', 'less']);
grunt.registerTask('default', ['less', 'watch']);
}
And I simply type grunt before to start working. It run a watcher and compiles my less files once something changes.
Edit:
There is also https://github.com/emberfeather/less.js-middleware but you need to add the compilation to the app's flow. This means that you will compile the less files during the run of the nodejs process. This will happen only once and if you make changes in some of the files you will not see the result. Of course you may want to compile on every request, but this will decrease the performance of your app. So, you will end up with some kind of a watcher and compiler. Exactly what Grunt is doing. If you don't want to run grunt every time you may add it to your boot scripts (or startup things under windows).
Depending on your arrangement, you may want to just look into less-middleware. It will compile your LESS into CSS on the fly in development, and in production will do it the first time the CSS is requested, and then serve up the CSS instead of recompiling it every time. Plenty of configuration examples at the included link.
I dont have the settings on hand for grunt-bootstrap and npmjs is offline right now for some reason, try this link for the settings https://npmjs.org/package/grunt-bootstrap
im using the latest version of grunt-bootstrap and grunt-contrib-less like this;
package.json
"devDependencies": {
"grunt": "~0.4.2",
"grunt-cli": "~0.1.11",
"grunt-bootstrap": "~0.1.0",
"grunt-contrib-jade": "~0.8.0",
"grunt-contrib-less": "~0.8.2",
"grunt-contrib-jshint": "~0.7.2",
"grunt-contrib-uglify": "~0.2.7",
"grunt-contrib-watch": "~0.5.3"
//others here
}
Gruntfile.JS less: entry
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
jshint: {
options: {
curly: true,
eqeqeq: true,
immed: true,
latedef: true,
newcap: true,
noarg: true,
sub: true,
undef: true,
boss: true,
eqnull: true,
browser: true,
globals: {
require: true,
define: true,
requirejs: true,
describe: true,
expect: true,
it: true
},
// https://leanpub.com/grunt/read #see 'module' is not defined
node: true
},
// https://leanpub.com/grunt/read #see 'module' is not defined
all: [
'Gruntfile.js',
'src/app/*.js',
'src/config.js',
'tests/app/*.js'
]
},
uglify: {
options: {
banner: '*//*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> *//*\n'
},
build: {
src: 'src/app/<%= pkg.name %>.js',
dest: 'build/app/<%= pkg.name %>.min.js'
}
},
// Run jshint any time a file is added
watch: {
scripts: {
files: [
'Gruntfile.js',
'src/app/*.js',
'tests/app/*.js'
],
tasks: ['jshint'],
options: {
/**
* If you need to dynamically modify your config, the spawn option must be disabled to keep the watch
* running under the same context.
*/
spawn: false
}
},
css: {
files: ['src/assets/css/*.less'],
tasks: ['less']
}
},
less: {
development: {
options: {
paths: ["src/assets/css"],
compress: true,
report: 'gzip',
cleancss: true,
ieCompat: true,
strictImports: true
//dumpLineNumbers
},
files: [{
expand: true,
cwd: "src/assets/css",
src: ['*.less'],
dest: 'build/assets/css',
ext: '.css'
}]
}
/*
production: {
options: {
cleancss: true
},
files: {
}
}*/
},
jade: {
compile: {
options: {
//client: false,
pretty: true,
data: {
debug: false
}
},
files: [ {
cwd: "src/app/views",
src: "**/*.jade",
dest: "build/templates",
expand: true,
ext: ".html"
} ]
}
}
});
// Load task(s)
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-jade');
grunt.loadNpmTasks('grunt-bootstrap');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
// Register task(s).
grunt.registerTask('default', [
'jshint',
'uglify',
'less',
'jade',
'bootstrap'
]);
grunt.registerTask('dev', [
'watch'
]);
};
Edit: i found this once again, https://github.com/jgallen23/grunt-bootstrap
i knew it was somewhere out there. there is some of your bootstrap config options your probably looking for that you need to add to the gruntfile.js to complete your task.. Enjoy

Resources