-
Notifications
You must be signed in to change notification settings - Fork 2
Workflow with Yeoman, Grunt and Bower
- Grunt Plugins
- Example 01 - Installation
- Example 02 - Simple example
- Example 03 - Static web server
- Example 04 - File watching and live reload
- Example 05 - Unit Testing with Jasmine
- Example 06 - Multi / Async task
- Example 07 - Build own grunt plugin
- Example 08 - Build custom jQuery
grunt-contrib-clean (v0.5.0) Clean files and folders.
grunt-contrib-compress (v0.5.3) Compress files and folders.
grunt-contrib-concat (v0.3.0) Concatenate files.
grunt-contrib-connect (v0.5.0) Start a connect web server.
grunt-contrib-copy (v0.4.1) Copy files and folders.
grunt-contrib-jasmine (v0.5.2) Run jasmine specs headlessly through PhantomJS.
grunt-contrib-jshint (v0.7.2) Validate files with JSHint.
grunt-contrib-uglify (v0.2.7) Minify files with UglifyJS.
grunt-contrib-watch (v0.5.3) Run predefined tasks whenever watched files changed.
Installation
//wrong way npm install -g grunt
npm install -g grunt-cli
Preparation and run grunt
mkdir example01
cd example01
grunt // Fatal error: Unable to find local grunt.
Install grunt locally and run again
npm install grunt
grunt // Fatal error: Unable to find Gruntfile.
Create Gruntfile.js
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({});
// A very basic default task.
grunt.registerTask('default', 'Log some stuff.', function() {
grunt.log.write('Logging some stuff...').ok();
});
};
or with grunt init https://github.com/gruntjs/grunt-init-gruntfile
grunt init:gruntfile
Remove your local grunt and add package.json
{
"name": "my-project-name",
"version": "0.1.0",
"devDependencies": {
"grunt": "~0.4.0",
"grunt-contrib-jshint": "~0.7.2",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-uglify": "~0.2.7"
}
}
Install it again ;)
npm install
Add Grunt Modules to package.json
{
"name": "my-project-name",
"version": "0.1.0",
"devDependencies": {
"grunt": "~0.4.0",
"grunt-contrib-jshint": "~0.7.2",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-uglify": "~0.2.7"
}
}
- JSHint for style checking and code analysis.
- Concat for file concatenation (for modules).
- JSUglify for minifying javascript files.
More Informations:
http://www.jshint.com/blog/jshint-3-plans/ https://github.com/mdevils/node-jscs https://github.com/mishoo/UglifyJS2 Change your grunt file
// Project configuration.
/*global module:false*/
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
jshint: {
src: {
options: {
curly: false,
undef: true
},
files: {
src: ['../app/src/app.js', '../app/src/lib.js']
}
}
},
concat: {
build: {
options: {
separator: ';',
},
src: ['../app/src/jquery.js' ,'../app/src/app.js', '../app/src/lib.js'],
dest: '../app/build/app.js'
}
},
uglify: {
build: {
files: { '../app/build/app.min.js': [ '../app/build/app.js' ] }
}
}
});
// Load task modules.
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
// Default task.
grunt.registerTask('default', 'jshint');
};
Run grunt
grunt // jshint task is logging errors
Fix JSHint problems with adding globals to options
globals: {
'window': true,
'jQuery': true
}
Add more task to the default task
grunt.registerTask('default', ['jshint', 'concat', 'uglify']);
Change task names:
grunt.registerTask('default', ['test']); // grunt
grunt.registerTask('test', ['jshint']); // grunt test
grunt.registerTask('build', ['test', 'concat', 'uglify']); // grunt build
Install new module
npm install grunt-contrib-connect --save-dev
Update Gruntfile
connect: {
server: {
options: {
port: 9001,
base: '../app'
}
}
}
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.registerTask('server', 'connect');
Run grunt
grunt // server is down after task finished
Change options to run server
keepalive: true
Add option to automatically open browser at server start
open: true
Install new module
npm install grunt-contrib-watch --save-dev
npm install grunt-contrib-connect --save-dev
npm install connect-livereload --save-dev
Update Gruntfile
connect: {
server: {
options: {
port: 9000,
base: '../app',
hostname: 'localhost', // Change to 0.0.0.0 to external connection.
open: true // Open default browser window.
}
}
}
watch: {
scripts: {
files: [ '../app/src/*.js' , '../app/index.html' ],
tasks: [] // Add more tasks here.
},
}
grunt.loadNpmTasks('grunt-contrib-connect');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', 'server');
grunt.registerTask('server', ['connect', 'watch']);
Add live reload with new connect middleware
middleware: function (connect, options) {
return [
require('connect-livereload')({ port: 35729 }),
// Serve static files.
connect.static(options.base)
];
}
Active live reload with watch task
options: {
livereload: 35729
}
- Jasmine http://localhost:9000/test-jasmine/SpecRunner.html
- QUnit http://localhost:9000/test-qunit/index.html
- Mocha http://localhost:9000/test-mocha/test/browser/opts.html
npm install grunt-contrib-watch --save-dev
npm install grunt-contrib-jasmine --save-dev
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-jasmine');
Change path to pivotal example tests.
path: '../app/test-jasmine'
Change configuration
watch: {
scripts: {
files: '<%= path %>/src/**/*.js',
tasks: ['jasmine']
},
},
jasmine: {
pivotal: {
src: '<%= path %>/src/**/*.js',
options: {
specs: '<%= path %>/spec/*Spec.js',
helpers: '<%= path %>/spec/*Helper.js'
}
}
}
Set default task and map test task to jasmine
grunt.registerTask('default', 'watch');
grunt.registerTask('test', 'jasmine');
Run grunt test
grunt test
Add code coverage report with istanbul (based on Esprima)
npm install grunt-template-jasmine-istanbul --save-dev
Change Jasmine options
template : require('grunt-template-jasmine-istanbul'),
templateOptions: {
coverage: 'reports/coverage.json',
report: 'reports/coverage'
}
For Grunt, Jasmine and RequireJS look at this example: https://github.com/jamespamplin/grunt-contrib-jasmine-requirejs-example
grunt taskName:targetName
Create task configuration with two targets.
grunt.initConfig({
task: {
targetFoo: [1, 2, 3],
targetBar: true
}
});
Register this task as multi task.
grunt.registerMultiTask('task', 'Log stuff.', function() {
grunt.log.writeln(this.target + ': ' + this.data);
});
Run grunt
grunt task:targetFoo // runs the task with the selected target
grunt task // goes over all targets
Add async task
grunt.registerTask('taskAsync', 'Async task.', function() {
// Force task into async mode and grab a handle to the "done" function.
var done = this.async();
// Run some sync stuff.
grunt.log.writeln('Processing task...');
// And some async stuff.
setTimeout(function() {
grunt.log.writeln('All done!');
done();
}, 1000);
});
Test execution order
grunt.registerTask('run', ['taskAsync', 'task:targetBar', 'taskAsync', 'task:targetFoo']);
grunt run
- Install grunt-init with
npm install -g grunt-init
- Install the gruntplugin template with
git clone git://github.com/gruntjs/grunt-init-gruntplugin.git ~/.grunt-init/gruntplugin
- Run
grunt-init gruntplugin
in an empty directory.
- Run
npm install
to prepare the development environment. - Author the plugin. Change
tasks/token_count.js
'use strict';
module.exports = function(grunt) {
grunt.registerMultiTask('token_count', 'Parse JavaScript and count tokens.', function() {
var esprima = require('esprima');
var histogram = {
Boolean: 0,
Identifier: 0,
Keyword: 0,
Null: 0,
Numeric: 0,
Punctuator: 0,
RegularExpression: 0,
String: 0
};
// Iterate over all specified file groups.
this.files.forEach(function(f) {
// Concat specified files.
var src = f.src.filter(function(filepath) {
// Warn on and remove invalid source files (if nonull was set).
if (!grunt.file.exists(filepath)) {
grunt.log.warn('Source file "' + filepath + '" not found.');
return false;
} else {
return true;
}
}).map(function(filepath) {
grunt.log.writeln('Token count for : ' + filepath);
// Read file source.
var content = grunt.file.read(filepath),
tokens = esprima.parse(content, { tokens: true }).tokens;
tokens.forEach(function (token) {
histogram[token.type] += 1;
});
});
for (var type in histogram) {
if (histogram.hasOwnProperty(type)) {
grunt.log.writeln(type + ' : ' + histogram[type]);
}
}
});
});
};
Add esprima to the dependencies at the package.json
"dependencies": {
"esprima": "~1.0.4"
}
- Create a local npm package.
cd grunt-token-count // folder where package.json exists
Create an tgz file
npm pack
Install the npm package from the tgz file
cd .. // path to your project
npm install grunt-token-count/grunt-token-count-0.1.0.tgz
Set the package to real private.
"private": true
Enter the jquery directory and install the Node dependencies, this time without specifying a global install:
cd jquery && npm install
Make sure you have grunt installed by testing:
grunt -version
Then, to get a complete, minified (w/ Uglify.js), linted (w/ JSHint) version of jQuery, type the following:
grunt
The built version of jQuery will be put in the dist/ subdirectory, along with the minified copy and associated map file.
Packets are loaded from an git endpoint and then cached locally. The packages are in this folder: yeoman-folder/components/
Versions of the packages are tags in the git repository.
"modernizr": "~2.6.1"
Create a github repository with an component.json
{
"name": "myProject",
"version": "1.0.0",
"main": ["./path/to/app.css", "./path/to/app.js"],
"dependencies": {
"jquery": "~1.7.2"
}
}
Register it global
bower register myawesomepackagename git://github.com/foo/bar
node_modules/bower/lib/core/source.js:line12
https://bower.herokuapp.com/packages
Move folder /bower_server to an Webserver
This url
http://localhost/bower_server/packages/
should return back this json (needs php)
[{"name":"fun","url":"[email protected]:netzzwerg/bower-fun.git"},
{"name":"core","url":"[email protected]:netzzwerg/bower-core.git"},
{"name":"jquery","url":"git://github.com/netzzwerg/bower-jquery.git"}]
and you can change the url in /fec_build/.bowerrc
{
"directory" : "packages",
"json" : "component.json",
"endpoint" : "http://localhost/bower_server"
}
bower search should you show only 4 results
cd fec_build
bower search
- Command Line Tools for Xcode http://developer.apple.com/downloads oder XCode
- Node.js >= 0.8.x http://nodejs.org/
- Ruby >= 1.8.7 (für Homebrew, Compass)
- Homebrew http://mxcl.github.com/homebrew/
ruby -e "$(curl -fsSkL raw.github.com/mxcl/homebrew/go)"
- git
- optipng
- jpegtran
- PhantomJS >= 1.6 http://phantomjs.org/index.html
brew install git optipng jpeg-turbo phantomjs
brew link jpeg-turbo
- Compass >= 0.12.1
gem update --system
gem install compass
- Yeoman
npm install yeoman -g
Check installation again
curl -L get.yeoman.io | bash
Installationspath
/usr/local/lib/node_modules/yeoman
These are equivalent to grunt alias except that we defined a single task and use arguments to trigger the appropriate target.
-
build
no html compression, no usemin-handler task -
usemin
(default) same as build but parsing config from markup -
text
same as usemin but without image (png/jpg) optimizing -
buildkit
minor html optimizations, all html whitespace/comments maintained (todo: inline script/style minified) -
basics
same as buildkit plus minor html optimizations -
test
same as default build plus but conditionally runs compass / manifest task depending on whether or not compass / phantomjs binaries are available within the path. During the checking process, we output warning infos about missing deps. It might make sense to make it the default (img task internally does this check)
-
default
rjs concat css min img rev usemin manifest -
usemin
usemin-handler rjs concat css min img rev usemin manifest -
text
usemin-handler rjs concat css min rev usemin manifest -
buildkit
usemin-handler rjs concat css min img rev usemin manifest html:buildkit -
basics
usemin-handler rjs concat css min img rev usemin manifest html:basics -
minify
usemin-handler rjs concat css min img rev usemin manifest html:compress -
test
usemin-handler rjs concat css min img rev usemin manifest
Show all existing generators.
yeoman init --help
Generate Backbone project
yeoman init backbone
Start local webserver
yeoman server
Test the application with Mocha
yeoman test
Build the application
yeoman build
Add new backbone model
yeoman init backbone:model MYDATA
Show all existing generators.
yeoman init --help
Generator path under MacOSX
/usr/local/lib/node_modules/yeoman/node_modules/yeoman-generators/lib/generators
generator-name/all/index.js
var path = require('path'),
util = require('util'),
yeoman = require('../../../../');
module.exports = Generator;
function Generator() {
yeoman.generators.Base.apply(this, arguments);
this.appname = path.basename(process.cwd());
}
util.inherits(Generator, yeoman.generators.Base);
Generator.prototype.createIndexFile = function createIndexFile() {
this.template('index.html', 'app/index.html');
};
Generator.prototype.createCssFile = function createCssFile() {
this.template('bootstrap.css', 'app/bootstrap.css');
};
Change your local grunt file:
grunt.registerTask('my-task', 'my grunt task', function() {
console.log('my task');
});
grunt.registerTask('build', 'your own build task list', function() {
//var tasks = 'clean mkdirs usemin-handler rjs concat min css rev usemin manifest copy time';
var tasks = 'my-task time';
grunt.task.run(tasks);
});
module.exports = function(grunt){
grunt.loadNpmTasks('your-grunt-plugin');
grunt.initConfig({
// grunt config
});
grunt.registerTask('default', 'your-task');
};