Grunt and Front End Development

I've heard a lot about Grunt.js and Gulp.js over the last year. It's been on my todo list of things to learn. Last week I jumped in with some help from Tad Ward via Skype.

I initially struggled as I had a project I was about to take on that was using gulp and I misunderstood what was needed. Turned out the files I was given were more examples of what was needed rather than me working with the existing code. Once I got that figured out, I decided to keep looking into both Grunt and Gulp. In the end I decided that Grunt looked easier for more.

I had previously installed Node and work in Git and Compass on all my projects so most of the requirements to get running were already set up on my system. Currently I've only got a testing project setup but how I see it working is that I open up a command prompt with Administrator permission (requires administrator permissions on Windows, not sure about Mac) and navigate to the project folder and then type npm init. There there will be some prompts that you need to fill out then you bare project is set.

The next step is to run the command:

1
npm install grunt --save-dev

The --save-dev installs grunt to this particular project. Once this is done, you then install the other grunt tasks that you want to run. The ones that I want to work with are Compass, Uglify, Watch and Concat. Next type the following commands:

1
2
3
4
npm install grunt-contrib-compass --save-dev
npm install grunt-contrib-uglify --save-dev
npm install grunt-contrib-watch --save-dev
npm install grunt-contrib-concat --save-dev

Next up we need to create a gruntfile which loads the commands and tasks that will be run. Through a lot of trial and error my working gruntfile is here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
module.exports = function(grunt) {
 
// Project configuration.
grunt.initConfig({
 pkg: grunt.file.readJSON('package.json'),
// CONFIG ===================================/
 
	watch: {
		compass: {
			files: ['**/*.{scss,sass}'],
			tasks: ['compass:dev']
		},
		js: {
			files: ['build/js/**/*.js', 'bower_components/*.js'],
			tasks: ['uglify', 'concat']
		},
 
		livereload: {
			options:{livereload:true},
			files:['css/*', '*.html', 'templates/**/*.html'],
		}
	},
 
	compass: {
		dev: {
		   options: {              
		       sassDir: ['build/sass'],
		       cssDir: ['css'],
		       environment: 'development'
		   }
		},
		prod: {
		   options: {              
		       sassDir: ['build/sass'],
		       cssDir: ['css'],
		       environment: 'production',
		       outputStyle: 'compact'
		   }
		},
 
	},
 
	uglify: {
		dev:{
			options:{
				compress: {
					drop_console: false
				}
			},
			files: {
		      'js/main.min.js': [
		      'build/js/*.js', 
		      'build/js/**/*.js'
		      ]
		  }
		},
		prod: {
			options:{
				compress: {
					drop_console: true
				}
			},
			files: {
		      'js/main.min.js': [
		      'build/js/*.js', 
		      'build/js/**/*.js'
		      ]
		  }
		},
 
	},
 
	// concatenate js and output unminified.
	concat: {
	    options: {
	      separator: '\r\n\r\n /******/ \r\n\r\n',
	    },
	    dist: {
	      src: ['build/js/*.js', 'build/js/**/*.js'],
	      dest: 'js/main.js',
	    },
	},
 
 
 
});
 
// DEPENDENT PLUGINS =========================/
 
grunt.loadNpmTasks('grunt-contrib-watch');
 
grunt.loadNpmTasks('grunt-contrib-compass');
 
grunt.loadNpmTasks('grunt-contrib-uglify');
 
grunt.loadNpmTasks('grunt-contrib-concat');
 
 
// TASKS =====================================/
 
grunt.registerTask('default', ['compass:dev' , 'uglify:dev' , 'concat', 'watch']);
 
};

What happens here is that the watch command Sets up files to watch and tasks to do. First it runs compass so that my Sass files get processed and output to the folders indicated in the Compass command below. Next in Watch js is uglified, i.e. compressed and concatenated. Lastly I use Concat to concatenate all the js files and output a version that is not minified. The minified version will be used in production. Line 76 adds line breaks between concatenated files for easier readability.

I do a lot of work for other agencies and feel that I should also provide them with a readable version of the javascript which this accomplishes. Now grunt outputs a main.js and a main.min.js file to the /js/ folder.

Back to the Watch command on line 18 we see:

18
19
20
21
livereload: {
	options:{livereload:true},
	files:['css/*', '*.html', 'templates/**/*.html'],
}

This is pure gold. Before I got this working I had gone after a red herring, grunt-browser-sync, which would never install and led to much frustration. Once I found the docs for Live Reload and set it up to reload css/js/html on change I was set. This command actually watches the CSS file for change. So once compass processes the Sass files and outputs the new CSS file then the browser reloads. There is a slight delay, but it is nice not having to manually refresh the page. To get it to work on your local files you need to either add a bit of javascript to the site or use a browser extension (Safari, Chrome, FireFox). Now all I do is make a change to the sass files, wait a second and the change is reflected on the site locally. No need for me to manually refresh the browser. This is brilliant. *UPDATE* - changed files for livereload and now works when developing in ExpressionEngine. I have my templates folder at public_html/templates - the /**/ is needed so that it will look for all html files inside all subdirectories.

When it's time to start development in the in the Command Prompt all you do is type grunt and then it performs the tasks set up on line 101.

101
grunt.registerTask('default', ['compass:dev' , 'uglify:dev' , 'concat', 'watch']);

Now when I'm ready to move the code to production I can run different commands for example grunt compass:prod will run the compass task but instead of adding line comments and keeping the css readable everything will be compressed with no comments. Additionally running grunt uglify:prod will take care of the js files and remove all console.logs, which are not really necessary on production.

Assuming you have a number of tasks that you like to use on every site, this whole process will be even easier. All you need to do is copy package.json and gruntfile.js of an existing project into a new project folder. Then open up your command prompt with administrator permission and navigate to the new folder and then enter this command:

1
npm install

Voila everything is installed and you are set to start developing. Comments and suggestions on tasks that may be useful to me or better approaches are appreciated.

Front End Syntax Highlighting With ExpressionEngine

Yesterday I was asked via twitter how I do syntax highlighting on this blog. It's actually quite simple with a couple of addons. I was using a different addon, but it stopped working when I upgraded the site last year and then switched to using EE Syntax by Eric Lamb Which does the bulk of what is needed.

My blog channel has Three custom fields - a blog image (file), a copy field (wygwam) and code sample (matrix). The matrix field contains all code blocks and then using the approach outlined here I place the code blocks into the wygwam field. I chose to use MX Jumper, but the other options will work just fine - this could also be done using Stash.

Not sure why, but when I try to post the template code that renders the template code the page doesn't load. Have tried several workarounds, but no go. Can post any code except the code that displays code. Instead check this pastie.

Git Ignore and ExpressionEngine

I've been working on trying to keep my git repos lean and only include files that need to be uploaded to the repo. Currently my gitignore file ignores ExpressionEngine cache folders, folders needed by CE Image to create dynamic images i.e. /images/remote and images/made the _thumbs folder used by expressionEngine in every upload directory and the backup folder used by DevDemon. Additionally I just added **/installer_OLD which is left over by DevDemon when doing an EE Update.

Below is my current gitignore file. If you have any suggestions for improvements, let me know in the comments. You should rename system and/or move it above root as well.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
## ignore cache
system/expressionengine/cache/*
!system/expressionengine/cache/gitkeep
 
## ignore dynamic image folders needed by CE Image
images/remote/*
images/made/*
 
## ignore thumbs folders
**/_thumbs
 
## ignore site backup folder Updater
/site_backup/*
!/site_backup/gitkeep
 
## don't ignore any file named gitkeep
!**/gitkeep
 
## ignore installer_old after updates using DevDemon Updater
**/installer_OLD

DevDemon Updater - enable only on localhost

I use the FocusLab Master Config setup to have my sites work in multiple environments. This works really great, however I also use Dev Demon's Updater to update addons and EE. Now I don't want to accidentally use updater on the live or staging site. So how do we prevent this while still being able to use updater on localhost. It's actually really simple.

From the master config setup open config.master.php and add the following - note 'disable', really this could be anything as long as it's not one of the real options

1
$env_config['updater']['file_transfer_method'] = 'disable'; // local, ftp, sftp

Then open config.local.php and add the following - you may need to adjust your third_party system and themes paths. I keep mine in /addons/themes and /addons/system/

1
2
3
4
5
6
7
8
9
10
	// DEVDEMON Updater
 
	$env_config['updater']['file_transfer_method'] = 'local'; // local, ftp, sftp
 
	$env_config['updater']['path_map']['root'] = $base_path; // Document Root
	$env_config['updater']['path_map']['backup'] = $base_path . '/site_backup/'; // Backup Dir
	$env_config['updater']['path_map']['system'] = APPPATH . '../'; // System Dir
	$env_config['updater']['path_map']['system_third_party'] = $base_path . '/addons/system/'; // Third Party dir system dir
	$env_config['updater']['path_map']['themes'] = $base_path    . '/themes/'; // Themes dir
	$env_config['updater']['path_map']['themes_third_party'] = $base_path . '/addons/themes/'; // Third Party dir in themes dir

Now you can use Updater locally and prevent mistakes from happening on your staging or live environments.

Your Voice Here
Leave a Comment