WORKFLOW PARA 
DESENVOLVIMENTO 
MOBILE USANDO 
GRUNT.JS 
davidson fellipe 
senior front-end engineer na globo.com
me 
html desde 2001 
senior front-end engineer 
globo.com ~ 2010 
mais em fellipe.com
POR QUE 
AUTOMATIZAMOS?
GRANDES 
PROBLEMAS PARA 
RESOLVER
PREGUIÇOSO 
!== 
EFICIENTE
GESTÃO DE DEPENDÊNCIAS, 
FRAMEWORKS MVC, TESTES, 
ANALISADORES DE QUALIDADE 
DE CÓDIGO, TASK RUNNERS, 
PERFORMANCE
TASK RUNNERS
VAMOS DE GRUNT?
GRUNT NÃO 
É O ÚNICO
MAKE 
ANT 
RAKE 
CAKE 
GULP 
SHELL 
JAVA 
RUBY 
COFFEE 
JS
ANT 
<cotamprgielet ">n ame="js-compile-all" description="Compile JavaScript files with Closure" unless="skip-js- 
<echo>Compiling JS files in ${input.scripts.dir} in closure...</echo> 
<apply executable="java" dest="${output.scripts.dir}"> 
<arg value="-jar"/> 
<arg path="${jar.lib.dir}/closure-compiler.jar"/> 
<arg line="--js"/> 
<srcfile/> 
<arg line="--js_output_file"/> 
<targetfile/> 
<fileset dir="${output.scripts.dir}" includes="**/*-main.debug.js" /> 
<mapper type="glob" from="*-main.debug.js" to="*-main.min.js"/> 
</apply> 
<echo>Finished compiling JS files</echo> 
MAKEFILE 
</target> xml 
http://mechanics.flite.com/blog/2012/06/19/why-we-use-node-dot-js-and-grunt-to-build-javascript/
http://i1-news.softpedia-static.com/images/news2/Three-of-the-Windows-Users-Fears-and-Misconceptions-About-Linux-427770-2.jpg
<3
grunt.js 
fácil de usar 
grande número de plugins 
imensa comunidade open source 
via linha de comando 
usa node.js
tasks 
testes 
pré-processadores 
jshint/csslint 
criar sprites 
concatenação 
otimização de imagens
https://github.com/gruntjs/grunt 
8420 STARS 
967 FORKS
http://npm-stat.vorba.ch/charts.html?package=grunt 
downloads 
600k 
400k 
200k 
oct nov dec jan feb mar apr may jun jul aug
COMO 
COMEÇAR ?
instalação node+npm 
$ npm install -g grunt-cli
configurar node?
$ make grunt-config 
grunt-config: 
@brew install node; #node 
@sudo curl https://npmjs.org/install.sh -k|sh;#npm 
@sudo npm install -g grunt-cli; #grunt 
@npm i --save-dev #dependencias 
MAKEFILE 
make
package 
.json { 
"name": "poll", 
"version": "0.0.1", 
"devDependencies": { 
"grunt": "~0.4.2", 
"grunt-contrib-watch": "~0.5.3", 
"load-grunt-tasks": "~0.2.0", 
"grunt-shell": “~0.6.1" 
} 
} 
js
instale com -- save-dev 
$ npm install nome-pacote --save-dev 
MAKEFILE 
terminal
.gitignore 
MAKEFILE 
.DS_Store 
... 
node_modules
Gruntfile 
.json 
module.exports = function(grunt) { 
grunt.initConfig({ 
pkg: grunt.file.readJSON('package.json'), 
pathSrc: 'src/', 
pathBuild: 'build/', 
compass: {}, 
shell: {} 
}); 
! 
grunt.loadNpmTasks(‘grunt-contrib-compass’); 
grunt.loadNpmTasks(‘grunt-contrib-shell’); 
grunt.loadNpmTasks(‘grunt-contrib-uglify’); 
grunt.registerTask('build', ['compass:min', 
'shell']); 
! 
}; 
js
INSTALE O 
LOAD-GRUNT-TASKS 
$ npm install load-grunt-tasks --save-dev
Gruntfile 
.json 
module.exports = function(grunt) { 
grunt.initConfig({ 
pkg: grunt.file.readJSON('package.json'), 
pathBase: 'static/poll/', 
compass: {}, 
minify: {}, 
uglify: {}, 
shell: {} 
}); 
// Load all tasks 
require('load-grunt-tasks')(grunt); 
grunt.registerTask('build', ['compass:min', 
'uglify', 
'shell']); 
}; 
js
$ grunt concat 
MAKEFILE 
concat:{ 
dist: { 
src: ['js/*.js'], 
dest: 'js/all.js' 
} 
}; 
js
grunt-contrib-compass 
a.scss 
e.scss 
i.scss 
o.scss 
u.scss 
vogais.css
grunt-contrib-compass 
$ npm install grunt-contrib-compass --save-dev 
MAKEFILE 
terminal
$ grunt compass:dev 
MAKEFILE 
grunt.initConfig({ 
compass: { 
dev: { 
options: { 
sassDir: 'src/scss', 
cssDir: 'build/css', 
imagesDir: 'src/img', 
generatedImagesDir: 'build/img' 
} 
}, 
prod: { /* ... */ } 
}}); js
$ grunt compass:prod 
MAKEFILE 
grunt.initConfig({ 
compass: { 
dev: { /* ... */ }, 
prod: { 
options: { 
sassDir: 'src/scss', 
cssDir: 'build/css', 
imagesDir: 'src/img', 
generatedImagesDir: 'build/img', 
outputStyle: 'compressed', 
noLineComments: true 
}}}}); js
executando 
$ grunt compass:dev 
$ grunt compass:prod
grunt-contrib-watch 
watch 
widget.scss widget.css
$ grunt watch 
MAKEFILE 
grunt.initConfig({ 
watch: { 
build: { 
files: [‘src/**/*.{scss, sass, js}'], 
tasks: [ 
'compass:dev', 'concat', 'uglify' 
] 
} 
} 
}); js
WATCH APENAS 
ARQUIVOS 
MODIFICADOS
newer:nomeDaTask 
MAKEFILE 
grunt.initConfig({ 
watch: { 
build: { 
files: [‘src/**/*.{scss, sass, js}'], 
tasks: [ 
'newer:compass:dev', 'newer:concat', 'newer:uglify' 
] 
} 
} 
}); js
$ grunt csslint 
MAKEFILE 
csslint:{ 
lax: { 
options: { 
csslintrc: '.csslintrc' 
}, 
src: ['css/*.css'] 
} 
}; js
$ grunt jshint 
MAKEFILE 
jshint: { 
options: { 
jshintrc: '.jshintrc' 
}, 
all: ['js/*.js'] 
}; 
js
$ grunt uglify 
MAKEFILE 
uglify: { 
dist: { 
files: { 
'js/all.min.js': ['js/all.js'] 
} 
} 
}; js
$ grunt imagemin 
MAKEFILE 
imagemin: { 
dist: { 
files: [{ 
expand: true, 
cwd: 'img', 
src: '{,*/}*.{png,jpg,jpeg}', 
dest: 'img' 
}]}}; js
$ grunt complexity 
MAKEFILE 
complexity: { 
src: ['<%= path %>js/*.js’], 
options: { 
breakOnErrors: true, 
errorsOnly: false, 
cyclomatic: [4, 8, 12], 
halstead: [10, 15, 20], 
maintainability: 100, 
hideComplexFunctions: false 
}} js
$ grunt complexity 
Running "complexity:generic" (complexity) task 
✗ src/js/c.js ███ 82.923 
✗ src/js/c.js:11 - <anonymous>.init is too complicated 
Cyclomatic: 21 
Halstead: 105.1875 
| Effort: 1.5177e+5 
| Volume: 1442.9 
| Vocabulary: 17 
MAKEFILE 
✓ src/js/b.js ███████ 141.28 js
https://github.com/vigetlabs/grunt-complexity
$ grunt concurrent 
imagemin:{ 
join: ['newer:sass:dev', 'newer:concat'], 
lint: ['newer:jshint', 'newer:csslint'], 
optim: ['newer:uglify', 'newer:imagemin'] 
}; 
MAKEFILE 
js
GRUNT.JS 
+ 
PHONEGAP
grunt-phonegap 
npm install phonegap -g 
wrapping para Phonegap 3.0 CLI 
https://github.com/logankoester/grunt-phonegap
$ grunt phonegap 
MAKEFILE 
phonegap: { 
config: { 
root: 'www', 
config: 'www/config.xml', 
cordova: '.cordova', 
html : 'index.html', // (Optional) You may change this to any other.html 
path: 'phonegap', 
cleanBeforeBuild: true // when false the build path doesn't get regenerated 
plugins: ['/local/path/to/plugin', 'http://example.com/path/to/plugin.git'], 
platforms: ['android'], 
maxBuffer: 200, // You may need to raise this for iOS. 
verbose: false, 
releases: 'releases', 
releaseName: function(){ 
var pkg = grunt.file.readJSON('package.json'); 
return(pkg.name + '-' + pkg.version); 
} 
debuggable: false, js
features 
App Icons 
versionCode 
Android Debugging 
splash screen 
permissões 
credenciais do build.phonegap.com 
mais em http://goo.gl/ozi4pf
https://github.com/davidsonfellipe/grunt-workflow
obrigado 
fellipe.com/talks 
github.com/davidsonfellipe 
twitter.com/davidsonfellipe

Workflow para desenvolvimento Web & Mobile usando grunt.js

  • 1.
    WORKFLOW PARA DESENVOLVIMENTO MOBILE USANDO GRUNT.JS davidson fellipe senior front-end engineer na globo.com
  • 2.
    me html desde2001 senior front-end engineer globo.com ~ 2010 mais em fellipe.com
  • 3.
  • 4.
  • 6.
  • 7.
    GESTÃO DE DEPENDÊNCIAS, FRAMEWORKS MVC, TESTES, ANALISADORES DE QUALIDADE DE CÓDIGO, TASK RUNNERS, PERFORMANCE
  • 8.
  • 9.
  • 11.
    GRUNT NÃO ÉO ÚNICO
  • 12.
    MAKE ANT RAKE CAKE GULP SHELL JAVA RUBY COFFEE JS
  • 13.
    ANT <cotamprgielet ">name="js-compile-all" description="Compile JavaScript files with Closure" unless="skip-js- <echo>Compiling JS files in ${input.scripts.dir} in closure...</echo> <apply executable="java" dest="${output.scripts.dir}"> <arg value="-jar"/> <arg path="${jar.lib.dir}/closure-compiler.jar"/> <arg line="--js"/> <srcfile/> <arg line="--js_output_file"/> <targetfile/> <fileset dir="${output.scripts.dir}" includes="**/*-main.debug.js" /> <mapper type="glob" from="*-main.debug.js" to="*-main.min.js"/> </apply> <echo>Finished compiling JS files</echo> MAKEFILE </target> xml http://mechanics.flite.com/blog/2012/06/19/why-we-use-node-dot-js-and-grunt-to-build-javascript/
  • 14.
  • 15.
  • 16.
    grunt.js fácil deusar grande número de plugins imensa comunidade open source via linha de comando usa node.js
  • 17.
    tasks testes pré-processadores jshint/csslint criar sprites concatenação otimização de imagens
  • 18.
  • 19.
    http://npm-stat.vorba.ch/charts.html?package=grunt downloads 600k 400k 200k oct nov dec jan feb mar apr may jun jul aug
  • 20.
  • 21.
    instalação node+npm $npm install -g grunt-cli
  • 22.
  • 23.
    $ make grunt-config grunt-config: @brew install node; #node @sudo curl https://npmjs.org/install.sh -k|sh;#npm @sudo npm install -g grunt-cli; #grunt @npm i --save-dev #dependencias MAKEFILE make
  • 24.
    package .json { "name": "poll", "version": "0.0.1", "devDependencies": { "grunt": "~0.4.2", "grunt-contrib-watch": "~0.5.3", "load-grunt-tasks": "~0.2.0", "grunt-shell": “~0.6.1" } } js
  • 25.
    instale com --save-dev $ npm install nome-pacote --save-dev MAKEFILE terminal
  • 26.
  • 27.
    Gruntfile .json module.exports= function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), pathSrc: 'src/', pathBuild: 'build/', compass: {}, shell: {} }); ! grunt.loadNpmTasks(‘grunt-contrib-compass’); grunt.loadNpmTasks(‘grunt-contrib-shell’); grunt.loadNpmTasks(‘grunt-contrib-uglify’); grunt.registerTask('build', ['compass:min', 'shell']); ! }; js
  • 28.
    INSTALE O LOAD-GRUNT-TASKS $ npm install load-grunt-tasks --save-dev
  • 29.
    Gruntfile .json module.exports= function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), pathBase: 'static/poll/', compass: {}, minify: {}, uglify: {}, shell: {} }); // Load all tasks require('load-grunt-tasks')(grunt); grunt.registerTask('build', ['compass:min', 'uglify', 'shell']); }; js
  • 30.
    $ grunt concat MAKEFILE concat:{ dist: { src: ['js/*.js'], dest: 'js/all.js' } }; js
  • 31.
    grunt-contrib-compass a.scss e.scss i.scss o.scss u.scss vogais.css
  • 32.
    grunt-contrib-compass $ npminstall grunt-contrib-compass --save-dev MAKEFILE terminal
  • 33.
    $ grunt compass:dev MAKEFILE grunt.initConfig({ compass: { dev: { options: { sassDir: 'src/scss', cssDir: 'build/css', imagesDir: 'src/img', generatedImagesDir: 'build/img' } }, prod: { /* ... */ } }}); js
  • 34.
    $ grunt compass:prod MAKEFILE grunt.initConfig({ compass: { dev: { /* ... */ }, prod: { options: { sassDir: 'src/scss', cssDir: 'build/css', imagesDir: 'src/img', generatedImagesDir: 'build/img', outputStyle: 'compressed', noLineComments: true }}}}); js
  • 35.
    executando $ gruntcompass:dev $ grunt compass:prod
  • 36.
  • 37.
    $ grunt watch MAKEFILE grunt.initConfig({ watch: { build: { files: [‘src/**/*.{scss, sass, js}'], tasks: [ 'compass:dev', 'concat', 'uglify' ] } } }); js
  • 38.
  • 39.
    newer:nomeDaTask MAKEFILE grunt.initConfig({ watch: { build: { files: [‘src/**/*.{scss, sass, js}'], tasks: [ 'newer:compass:dev', 'newer:concat', 'newer:uglify' ] } } }); js
  • 40.
    $ grunt csslint MAKEFILE csslint:{ lax: { options: { csslintrc: '.csslintrc' }, src: ['css/*.css'] } }; js
  • 41.
    $ grunt jshint MAKEFILE jshint: { options: { jshintrc: '.jshintrc' }, all: ['js/*.js'] }; js
  • 42.
    $ grunt uglify MAKEFILE uglify: { dist: { files: { 'js/all.min.js': ['js/all.js'] } } }; js
  • 43.
    $ grunt imagemin MAKEFILE imagemin: { dist: { files: [{ expand: true, cwd: 'img', src: '{,*/}*.{png,jpg,jpeg}', dest: 'img' }]}}; js
  • 44.
    $ grunt complexity MAKEFILE complexity: { src: ['<%= path %>js/*.js’], options: { breakOnErrors: true, errorsOnly: false, cyclomatic: [4, 8, 12], halstead: [10, 15, 20], maintainability: 100, hideComplexFunctions: false }} js
  • 45.
    $ grunt complexity Running "complexity:generic" (complexity) task ✗ src/js/c.js ███ 82.923 ✗ src/js/c.js:11 - <anonymous>.init is too complicated Cyclomatic: 21 Halstead: 105.1875 | Effort: 1.5177e+5 | Volume: 1442.9 | Vocabulary: 17 MAKEFILE ✓ src/js/b.js ███████ 141.28 js
  • 46.
  • 47.
    $ grunt concurrent imagemin:{ join: ['newer:sass:dev', 'newer:concat'], lint: ['newer:jshint', 'newer:csslint'], optim: ['newer:uglify', 'newer:imagemin'] }; MAKEFILE js
  • 48.
  • 50.
    grunt-phonegap npm installphonegap -g wrapping para Phonegap 3.0 CLI https://github.com/logankoester/grunt-phonegap
  • 51.
    $ grunt phonegap MAKEFILE phonegap: { config: { root: 'www', config: 'www/config.xml', cordova: '.cordova', html : 'index.html', // (Optional) You may change this to any other.html path: 'phonegap', cleanBeforeBuild: true // when false the build path doesn't get regenerated plugins: ['/local/path/to/plugin', 'http://example.com/path/to/plugin.git'], platforms: ['android'], maxBuffer: 200, // You may need to raise this for iOS. verbose: false, releases: 'releases', releaseName: function(){ var pkg = grunt.file.readJSON('package.json'); return(pkg.name + '-' + pkg.version); } debuggable: false, js
  • 52.
    features App Icons versionCode Android Debugging splash screen permissões credenciais do build.phonegap.com mais em http://goo.gl/ozi4pf
  • 54.
  • 55.