Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Grunting Maintainability
Search
Philip Zastrow
April 02, 2014
Programming
1
380
Grunting Maintainability
Overview of using the Grunt JS task runner and working with some useful Grunt Plugins.
Philip Zastrow
April 02, 2014
Tweet
Share
More Decks by Philip Zastrow
See All by Philip Zastrow
The One Slide Deck
zastrow
0
9
The 4-Slide Deck
zastrow
0
11
Mixin It Up
zastrow
1
85
Designing Responsively from Mobile to HD
zastrow
6
280
Pushing Pixels to Cranking Code
zastrow
3
1.9k
Other Decks in Programming
See All in Programming
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
360
250830 IaCの選定~AWS SAMのLambdaをECSに乗り換えたときの備忘録~
east_takumi
0
390
知っているようで知らない"rails new"の世界 / The World of "rails new" You Think You Know but Don't
luccafort
PRO
1
110
今だからこそ入門する Server-Sent Events (SSE)
nearme_tech
PRO
3
180
個人軟體時代
ethanhuang13
0
320
Ruby×iOSアプリ開発 ~共に歩んだエコシステムの物語~
temoki
0
270
Zendeskのチケットを Amazon Bedrockで 解析した
ryokosuge
3
300
ProxyによるWindow間RPC機構の構築
syumai
3
1.2k
Amazon RDS 向けに提供されている MCP Server と仕組みを調べてみた/jawsug-okayama-2025-aurora-mcp
takahashiikki
1
110
為你自己學 Python - 冷知識篇
eddie
1
350
私の後悔をAWS DMSで解決した話
hiramax
4
210
請來的 AI Agent 同事們在寫程式時,怎麼用 pytest 去除各種幻想與盲點
keitheis
0
120
Featured
See All Featured
How to Ace a Technical Interview
jacobian
279
23k
BBQ
matthewcrist
89
9.8k
GitHub's CSS Performance
jonrohan
1032
460k
Optimising Largest Contentful Paint
csswizardry
37
3.4k
Done Done
chrislema
185
16k
Speed Design
sergeychernyshev
32
1.1k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
30
9.7k
Art, The Web, and Tiny UX
lynnandtonic
303
21k
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
Building Adaptive Systems
keathley
43
2.7k
Optimizing for Happiness
mojombo
379
70k
Docker and Python
trallard
45
3.6k
Transcript
Philip Zastrow GRUNTING MAINTAINABILITY @zastrow
ROBOTS A CHILDHOOD MEMORY OF
THERE WAS A ROBOT AT MY GRAMPA’S WORKPLACE
THE ARM HELPED MAKE COMPUTER PROCESSORS
MY GRAMPA DIDN’T DESIGN THE ROBOT
None
HIS PART TURNED MANY SMALL FUNCTIONS INTO A ROBOT
GRUNTING MAINTAINABILITY FRONTEND TOOLS
HTML CSS JS WHAT WE BUILD
HBS SCSS COFFEE HOW WE BUILD
sass input.scss output.css sass watch SCSS CSS
coffee --compile coffee --watch COFFEE JS
ONE COMMAND ONE FUNCTION
WE NEED A FRONTEND ROBOT
None
None
grunt grunt watch COFFEE JS SCSS CSS
ONE COMMAND MANY FUNCTIONS
WHAT IS GRUNT?
Grunt is a tool that binds your other tools together
GRUNT IS A TASK RUNNER
THERE IS A BUFFET OF TASK RUNNERS
HELPS AUTOMATE TEDIOUS WORK GRUNT IS A TASK RUNNER
GRUNT AUTOMATES RUNNING TESTS
GRUNT AUTOMATES RUNNING TESTS FILE CONVERSION
GRUNT AUTOMATES RUNNING TESTS FILE CONVERSION CONCATENATION
nodejs.org brew install node
Grunt for People Who Think Things Like Grunt are Weird
and Hard —Chris Coyier, 24 Ways 24ways.org/2013/grunt-is-not-weird-and-hard
HOW DOES GRUNT WORK?
gruntjs.com/getting-started npm install -g grunt-cli GRUNT CLI
grunt
package.json Node devDependencies i.e. plugins
{ "name": “sample-site", "version": "0.1.0", "devDependencies": { "grunt": "~0.4.2", "assemble":
"~0.4.13", "grunt-contrib-clean": "~0.4.0", "grunt-contrib-coffee": "~0.6.0", "grunt-contrib-compass": "~0.1.3", "grunt-contrib-copy": "~0.4.1" } }
npm install
npm install grunt-plugin --save-dev
.js or .coffee GRUNTFILE The list of instructions for each
plugin
module.exports = function(grunt) { // Project configuration. grunt.initConfig({ 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'); // Default task(s). grunt.registerTask('default', ['uglify']); }; Example code from gruntjs.com
module.exports = (grunt) -> # Project configuration. grunt.initConfig pkg: require("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' # Default task(s). grunt.registerTask 'default', ['uglify'] Example code from gruntjs.com
24ways.org/2013/grunt-is-not-weird-and-hard gruntjs.com
GRUNTING MAINTAINABILITY BUILDING MAINTAINABLE WEBSITES WITH GRUNT
FRONTEND DEVELOPMENT AND HOW WE BUILD SITES
Focus primarily on HTML, CSS, and JS; Not a CMS
Any one teammate could jump on any project at anytime
Grunt helps us collaborate with each other and client dev
teams
We use Grunt on our own website seesparkbox.com
We like improving workflow, so all tools are eligible for
replacement
We like improving workflow, so all tools are eligible for
replacement …including Grunt
Frontend projects start with a Grunt scaffolding
Grunt Project coffee data dist features grunt public Gemfile Gruntfile
config.rb package.json .gitignore bower.json .bowerrc sass specs templates
Grunt Project coffee data dist features grunt public Gemfile Gruntfile
config.rb package.json .gitignore bower.json .bowerrc sass specs templates
Grunt Project coffee data dist public sass templates
GRUNT PLUGINS SPARKBOX USES
Grunt plugins are Grunt-wrapped preprocessors
Grunt Project coffee data dist public sass templates ASSEMBLE HTML
npm install assemble --save-dev assemble.io
module.exports = (grunt) -> grunt.config "assemble", options: partials: "templates/partials/*" data:
"data/*.yml" layoutdir: "templates/layouts/" layout: ['default.hbs'] files: expand: true cwd: 'templates/pages' src: ['*.hbs'] dest: './dist/' ! grunt.loadNpmTasks 'assemble'
HANDLEBARS HTML STRUCTURE handlebarsjs.com
module.exports = (grunt) -> grunt.config "assemble", options: partials: "templates/partials/*" data:
"data/*.yml" layoutdir: "templates/layouts/" layout: ['default.hbs'] files: expand: true cwd: 'templates/pages' src: ['*.hbs'] dest: './dist/' ! grunt.loadNpmTasks 'assemble'
Grunt Project coffee data dist public sass templates
templates layouts pages partials
templates layouts pages partials layoutdir: "templates/layouts/"
module.exports = (grunt) -> grunt.config "assemble", options: partials: "templates/partials/*" data:
"data/*.yml" layoutdir: "templates/layouts/" layout: ['default.hbs'] files: expand: true cwd: 'templates/pages' src: ['*.hbs'] dest: './dist/' ! grunt.loadNpmTasks 'assemble'
layout default.hbs contact.hbs layout: ['default.hbs']
<!doctype html> <html> <head> <title> Sample Site </title> <head> <body>
{{> _site--header }} {{> body }} {{> _site--footer }} </body> </html>
<!doctype html> <html> <head> <title> Sample Site </title> <head> <body>
{{> _site--header }} {{> body }} {{! Loads in Page Content }} {{> _site--footer }} </body> </html>
<!doctype html> <html> <head> <title> Sample Site </title> <head> <body>
{{> _site--header }} {{! Loads Site Header Partial }} {{> body }} {{> _site--footer }} {{! Loads Site Footer Partial }} </body> </html>
module.exports = (grunt) -> grunt.config "assemble", options: partials: "templates/partials/*" data:
"data/*.yml" layoutdir: "templates/layouts/" layout: ['default.hbs'] files: expand: true cwd: 'templates/pages' src: ['*.hbs'] dest: './dist/' ! grunt.loadNpmTasks 'assemble'
templates layout pages partials cwd: 'templates/pages'
pages index.hbs about.hbs contact.hbs
module.exports = (grunt) -> grunt.config "assemble", options: partials: "templates/partials/*" data:
"data/*.yml" layoutdir: "templates/layouts/" layout: ['default.hbs'] files: expand: true cwd: 'templates/pages' src: ['*.hbs'] dest: './dist/' ! grunt.loadNpmTasks 'assemble'
index.hbs about.hbs contact.hbs cwd: 'templates/pages' src: ['*.hbs'] dest: './dist/' index.html
about.html contact.html
<div class="main_content"> <article class="main_content--article"> <header class="main_content--header"> <h1 class="main_content--title">Our Homepage</h1> <header>
<div class="article_content"> <p>This is the main content for our page.</p> </div> </article> </div> ! {{> _site--aside }}
module.exports = (grunt) -> grunt.config "assemble", options: partials: "templates/partials/*" data:
"data/*.yml" layoutdir: "templates/layouts/" layout: ['default.hbs'] files: expand: true cwd: 'templates/pages' src: ['*.hbs'] dest: './dist/' ! grunt.loadNpmTasks 'assemble'
templates layout pages partials partials: "templates/partials/*"
partials _site--aside.hbs _site--footer.hbs _site--header.hbs _site--navigation.hbs
<header class="site_header"> <h1 class="site_header--title">Sample Site</h1> {{> _site--navigation }} <header>
<nav class="site_nav"> <ul class="site_nav--list"> {{#each site-nav.nav-list}} <li class="site_nav--item"> <a href=“{{target}}"
class="site_nav--link"> {{nav_item}} </a> </li> {{/each}} </ul> <nav>
module.exports = (grunt) -> grunt.config "assemble", options: partials: "templates/partials/*" data:
"data/*.yml" layoutdir: "templates/layouts/" layout: ['default.hbs'] files: expand: true cwd: 'templates/pages' src: ['*.hbs'] dest: './dist/' ! grunt.loadNpmTasks 'assemble'
YAML HTML CONTENT yaml.org
YAML YAML Ain’t Markup Language yaml.org
Grunt Project coffee data dist public sass templates data: "data/*.yml"
data index.yml about.yml site-info.yml site-nav.yml
nav-list: - nav_item: Home target: "/" - nav_item: About target:
"about.html" - nav_item: Blog target: "blog.html" - nav_item: Contact target: "contact.html"
<nav class="site_nav"> <ul class="site_nav--list"> {{#each site-nav.nav-list}} <li class="site_nav--list"> <a href=“{{target}}"
class="site_nav--list"> {{nav_item}} </a> </li> {{/each}} </ul> <nav>
<nav class="site_nav"> <ul class="site_nav--list"> <li class="site_nav--list"> <a href="/" class="site_nav--list"> Home
</a> </li> <li class="site_nav--list"> <a href="about.html" class="site_nav--list"> About </a> </li> <li class="site_nav--list"> <a href="blog.html" class="site_nav--list"> Blog </a> </li> <li class="site_nav--list"> <a href="contact.html" class="site_nav--list"> {{nav_item}} </a>
<!doctype html> <html> <head> <title> Sample Site </title> <head> <body>
{{> _site-header }} {{> body }} {{> _site-footer }} </body> </html>
<!doctype html> <html> <head> <title> Sample Site </title> <head> <body>
{{> _site-header }} {{> body }} {{> _site-footer }} </body> </html>
<!doctype html> <html> <head> <title> Sample Site{{#if page_title}} | {{page_title}}{{/if}}
</title> <head> <body> {{> _site-header }} {{> body }} {{> _site-footer }} </body> </html>
YAML FRONT MATTER
--- page_title: About Us --- ! <div class="main_content"> <article class="main_content--article">
<header class="main_content--header"> <h1 class="main_content—title">{{page_title}}</h1> <header> <div class="article_content"> {{ about.page-content }} </div> </article> </div> ! {{> _site--aside }}
<!doctype html> <html> <head> <title> Sample Site{{#if page_title}} | {{page_title}}{{/if}}
</title> <head> <body> {{> _site-header }} {{> body }} {{> _site-footer }} </body> </html>
<!doctype html> <html> <head> <title> Sample Site | About Us
</title> <head> <body> <header class="site_header"> <h1 class="site_header--title">Sample Site</h1> <nav class="site_nav"> <ul class="site_nav--list"> <li class="site_nav--list"> <a href="/" class="site_nav—list"> Home </a> </li> <li class="site_nav--list"> <a href="about.html" class="site_nav--list"> About </a> </li> <li class="site_nav--list">
Grunt Project coffee data dist public sass templates SASS with
COMPASS CSS
npm install grunt-contrib-compass --save-dev github.com/gruntjs/grunt-contrib-compass
module.exports = (grunt) -> grunt.config "compass", dev: options: environment: "dev"
dist: options: environment: "production" grunt.loadNpmTasks "grunt-contrib-compass"
sass _general-extends.scss _general-mixins.scss _general-variables.scss _site--header.scss _site--navigation.scss mq-base.scss nomq-base.scss
Grunt Project coffee data dist public sass templates COFFEESCRIPT JAVASCRIPT
npm install grunt-contrib-coffee --save-dev github.com/gruntjs/grunt-contrib-coffee
module.exports = (grunt) -> grunt.config "coffee", compile: files: "dist/js/app.js": "coffee/app.coffee"
grunt.loadNpmTasks "grunt-contrib-coffee"
Grunt Project coffee data dist public sass templates
Grunt Project coffee data dist public sass templates
Grunt Project coffee data dist public sass templates CLEANED OUT
BEFORE THE SITE GENERATES
npm install grunt-contrib-clean --save-dev github.com/gruntjs/grunt-contrib-clean
module.exports = (grunt) -> grunt.config "clean", all: src: "dist/*" dot:
true #clean hidden files as well ! grunt.loadNpmTasks 'grunt-contrib-clean'
Grunt Project coffee data dist public sass templates STATIC FILES
NO PREPROCESSORS
npm install grunt-contrib-copy --save-dev github.com/gruntjs/grunt-contrib-copy
module.exports = (grunt) -> grunt.config "copy", main: files: [ expand:
true cwd: "public/" src: ["**"] dest: "dist/" ] ! grunt.loadNpmTasks 'grunt-contrib-copy'
Keep files short and let Grunt string them together
GRUNT PLUGIN ONE MORE THING!
Grunt Project coffee data dist public sass templates grunticon SVG
WITH PNG FALLBACK
npm install grunt-grunticon --save-dev github.com/filamentgroup/grunticon
module.exports = (grunt) -> grunt.config "grunticon", icons: files: [ expand:
true, cwd: "grunticon/src", src: ["*.svg"], dest: "dist/grunticon" ] ! grunt.loadNpmTasks "grunt-grunticon"
grunticon src
grunticon sample-logo.svg
grunticon png src grunticon.loader.txt icons.data.png.css icons.data.svg.css icons.fallback.css preview.html
.icon-sample-logo { background-image: url(‘data:image/svg +xml;charset=US-ASCII,%3C%3Fxml %20version%3D %221.0%22%20encoding%3D %22UTF-8%22%20standalone%3D%22no%22%3F%3E%3Csvg %20width%3D%22302px%22%20height%3D%2266px %22%20viewBox%3D%220%200%20302%2066%22%20version%3D
%221.1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org %2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F %2Fwww.w3.org%2F1999%2Fxlink%22%3E %20%20%20%20%3Ctitle%3EButton%3C%2Ftitle%3E %20%20%20%20%3Cdefs%3E%20%20%20%20%20%20%3Cstyle%3E %20%20%20%20%20%20%20%20.button--path%20%7B %20%20%20%20%20%20%20%20%20%20fill%3A%20%23D4D4D4%3B %20%20%20%20%20%20%20%20%7D%20%20%20%20%20%20%3C %2Fstyle%3E%20%20%20%20%3C%2Fdefs%3E %20%20%20%20%3Cpath%20class%3D%22button--path%22%20d %3D %22M244.681203%2C0%20L56.2445924%2C0%20C27.7845831%2C 0%200%2C33.8953123%200%2C33.8953123%20C0%2C33.8953123 %2026.3926138%2C65.9999996%2055.4422152%2C65.9999996% 20C137.527029%2C65.9999996%20218.318013%2C66.0000005%
.icon-sample-logo { background-image: url(‘data:image/ png;base64,iVBORw0KGgoAAAANSUhEUgAAAS4AAABCCAYAAADpAT vWAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEwAACxMBAJqcGAA ACGpJREFUeJzt3XuMXGUZBvDn/Wa6uy5toS0bE2OUEly3O +eMVrZaFdRN1CbEpqYNBaHBGAtyCaigTRo0AaoxBIsXEoLIRZRU2F CQgEgpcYlJCZcRh3POzGxWCAIBQlgSUzRMpzPv6x/ uku2ys7tzPbPd5/ff5sx5v2f/
eXL22znnCBa5IAhOBTDknDvdzNYCWG1mawCsBrDaObc83oRE7aWqJ efcuwAOq+rrzrnXALxgZhGAoFAoRNu3b6/ EHLMhEneAWgVBkHbOfR3A51X1dOfcmrgzES0mqvqOc+4pAKNm9ojv +8/ HnalWi6K4oij6NIBtqrrVOXda3HmIjjOvArgfwD7P856JO8xCdGxx BUGwSkQuBHCJiJwScxyiJUFV/ ykiv122bNmdAwMDE3HnqabjiiuXy6VU9QoR2QGgN +48REvR5D7ZPaq6N51OB3HnmaljiiuXy33GzK4D8NW4sxDRMQ6Y2R 7f9w/ FHWRK7MU1eYX1UxHZEncWIqrOzB53zv0olUo9HXeW2IqrUCicUqlU rlPV851zLq4cRFQbVb3PObfb87wX4srQ9uKKoqjLzHaZ2dXOuZ52r 09EjZvcA/ slgD2e5/2n3eu3tbjCMBwWkZsBDLRzXSJqDVV9HcD30+n0SDvXbUt
.icon-sample-logo { background-image: url(‘png/sample-logo.png'); background-repeat: no-repeat; }
data URI
Make sure SVG files are optimized github.com/svg/svgo
A Bit About SVG seesparkbox.com/foundry/a_bit_about_svg
GRUNTING MAINTAINABILITY REVIEW
None
GRUNT PLUGINS
ASSEMBLE HTML GENERATOR assemble.io npm install assemble --save-dev
SASS/COMPASS CSS GENERATOR github.com/gruntjs/contrib-grunt-compass npm install contrib-grunt-compass --save-dev
COFFEESCRIPT JS GENERATOR github.com/gruntjs/contrib-grunt-coffee npm install contrib-grunt-coffee --save-dev
CLEAN REMOVES ALL THE THINGS github.com/gruntjs/contrib-grunt-clean npm install contrib-grunt-clean --save-dev
COPY ADDS ALL THE THINGS github.com/gruntjs/contrib-grunt-copy npm install contrib-grunt-copy --save-dev
GRUNTICON SVG WITH PNG FALLBACK github.com/filamentgroup/grunticon npm install grunt-grunticon --save-dev
The goal is simplified and concise files
THAT’S GRUNTING MAINTAINABILITY
THANKS! @zastrow
[email protected]