├── lib ├── plugins │ ├── img.js │ ├── rev.js │ ├── index.js │ ├── link.js │ └── script.js ├── support │ └── reload.js └── utils │ ├── index.js │ └── fetch.js ├── test ├── fixtures │ ├── img │ │ └── expected │ │ │ ├── .gitignore │ │ │ ├── 1.png │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ ├── 4.png │ │ │ ├── 5.jpg │ │ │ └── 6.jpg │ ├── init │ │ ├── expected │ │ │ ├── .gitattributes │ │ │ ├── robots.txt │ │ │ ├── apple-touch-icon-114x114-precomposed.png │ │ │ ├── package.json │ │ │ ├── .gitignore │ │ │ ├── crossdomain.xml │ │ │ ├── LICENSE-MIT │ │ │ ├── humans.txt │ │ │ ├── apple-touch-icon.png │ │ │ ├── apple-touch-icon-precomposed.png │ │ │ ├── apple-touch-icon-57x57-precomposed.png │ │ │ ├── apple-touch-icon-72x72-precomposed.png │ │ │ ├── readme.md │ │ │ ├── favicon.ico │ │ │ ├── 404.html │ │ │ ├── index.html │ │ │ ├── apple-touch-icon-144x144-precomposed.png │ │ │ └── grunt.js │ │ └── rjs.gruntfile.js │ ├── rjs │ │ ├── expected │ │ │ ├── sub │ │ │ │ └── betaSub.js │ │ │ ├── alpha.js │ │ │ ├── beta.js │ │ │ ├── main.js │ │ │ └── plugins.js │ │ ├── sample │ │ │ ├── sub │ │ │ │ └── betaSub.js │ │ │ ├── main.js │ │ │ ├── alpha.js │ │ │ └── beta.js │ │ └── grunt.js │ ├── rjs-almond │ │ ├── expected │ │ │ ├── sub │ │ │ │ └── betaSub.js │ │ │ ├── alpha.js │ │ │ ├── beta.js │ │ │ ├── plugins.js │ │ │ └── main.js │ │ ├── sample │ │ │ ├── sub │ │ │ │ └── betaSub.js │ │ │ ├── main.js │ │ │ ├── alpha.js │ │ │ └── beta.js │ │ └── grunt.js │ ├── css │ │ ├── ui │ │ │ └── widget.css │ │ ├── grunt.js │ │ ├── app.css │ │ ├── style.css │ │ └── expected.css │ ├── tar │ │ └── test.tgz │ ├── default │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.jpg │ │ ├── 6.jpg │ │ ├── grunt.js │ │ ├── build.minify.html │ │ ├── index.html │ │ ├── expected.html │ │ └── usemin.html │ ├── usemin │ │ ├── tmpl.hbs │ │ ├── tmpl.mustache │ │ ├── expected │ │ │ ├── tmpl.hbs │ │ │ └── tmpl.mustache │ │ ├── index.html │ │ └── reved.html │ └── html │ │ └── index.html ├── mocha.opts ├── features │ ├── template.feature │ ├── docs.feature │ ├── rjs.feature │ ├── img.feature │ ├── tar.feature │ ├── init.feature │ ├── css.feature │ ├── default.feature │ ├── html.feature │ ├── usemin.feature │ └── steps │ │ └── default.js └── tasks │ ├── minify.js │ ├── default.js │ ├── test-docs.js │ ├── test-html.js │ ├── test-img.js │ ├── test-tar.js │ ├── test-css.js │ ├── test-init.js │ ├── test-rjs.js │ └── test-usemin.js ├── vendor ├── optipng-0.7.1-win32 │ ├── OptiPNG.url │ ├── optipng.exe │ └── doc │ │ ├── authors.txt │ │ ├── optipng.man.pdf │ │ ├── license.txt │ │ └── todo.txt └── jpegtran-8d │ └── jpegtran.exe ├── .gitignore ├── .gitmodules ├── support ├── docs │ ├── pages.txt │ ├── package.json │ └── readme.md ├── pre-commit ├── grunt-docs.js ├── grunt-inspect.js └── grunt-test.js ├── readme.md ├── h5bp.js ├── tasks ├── util.js ├── init │ ├── templates │ │ ├── rjs.json │ │ └── defaults.json │ └── h5bp │ │ ├── prompts.txt │ │ ├── notes.txt │ │ ├── readme.md │ │ └── gruntfile.js ├── docs.js ├── rev.js ├── rjs.js ├── css.js ├── h5bp.js ├── tar.js ├── html.js ├── serve.js ├── dom.js └── img.js ├── LICENSE.md ├── package.json ├── bin ├── h5bp └── help.txt ├── docs ├── api │ ├── lib │ │ └── docs │ │ │ ├── img.html │ │ │ └── rev.html │ └── tasks │ │ └── docs │ │ └── main.html └── css │ ├── white.css │ └── grey.css ├── grunt.js └── documentation.md /lib/plugins/img.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/plugins/rev.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/img/expected/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/init/expected/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /test/fixtures/rjs/expected/sub/betaSub.js: -------------------------------------------------------------------------------- 1 | define({name:"betaSubName"}) -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --timeout 15000 3 | --slow 1500 4 | -------------------------------------------------------------------------------- /test/fixtures/rjs-almond/expected/sub/betaSub.js: -------------------------------------------------------------------------------- 1 | define({name:"betaSubName"}) -------------------------------------------------------------------------------- /test/fixtures/rjs/sample/sub/betaSub.js: -------------------------------------------------------------------------------- 1 | define({ 2 | name: 'betaSubName' 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixtures/css/ui/widget.css: -------------------------------------------------------------------------------- 1 | .css-reved .img-5 { background: url('../../img/5.jpg'); } 2 | -------------------------------------------------------------------------------- /test/fixtures/rjs-almond/sample/sub/betaSub.js: -------------------------------------------------------------------------------- 1 | define({ 2 | name: 'betaSubName' 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixtures/rjs/expected/alpha.js: -------------------------------------------------------------------------------- 1 | define(["require","exports","module"],function(a,b){b.name="alpha"}) -------------------------------------------------------------------------------- /test/fixtures/rjs/sample/main.js: -------------------------------------------------------------------------------- 1 | require(['alpha', 'beta'], function (alpha, beta) { 2 | }); 3 | 4 | -------------------------------------------------------------------------------- /vendor/optipng-0.7.1-win32/OptiPNG.url: -------------------------------------------------------------------------------- 1 | [InternetShortcut] 2 | URL=http://optipng.sourceforge.net/ 3 | -------------------------------------------------------------------------------- /test/fixtures/rjs-almond/sample/main.js: -------------------------------------------------------------------------------- 1 | require(['alpha', 'beta'], function (alpha, beta) { 2 | }); 3 | 4 | -------------------------------------------------------------------------------- /test/fixtures/rjs/expected/beta.js: -------------------------------------------------------------------------------- 1 | define(["./sub/betaSub"],function(a){return{name:"beta",subName:a.name}}) -------------------------------------------------------------------------------- /test/fixtures/rjs-almond/expected/alpha.js: -------------------------------------------------------------------------------- 1 | define(["require","exports","module"],function(a,b){b.name="alpha"}) -------------------------------------------------------------------------------- /test/fixtures/rjs/sample/alpha.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports) { 2 | exports.name = 'alpha'; 3 | }); 4 | -------------------------------------------------------------------------------- /test/fixtures/tar/test.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/tar/test.tgz -------------------------------------------------------------------------------- /test/fixtures/default/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/default/1.png -------------------------------------------------------------------------------- /test/fixtures/default/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/default/2.png -------------------------------------------------------------------------------- /test/fixtures/default/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/default/3.png -------------------------------------------------------------------------------- /test/fixtures/default/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/default/4.png -------------------------------------------------------------------------------- /test/fixtures/default/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/default/5.jpg -------------------------------------------------------------------------------- /test/fixtures/default/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/default/6.jpg -------------------------------------------------------------------------------- /test/fixtures/rjs-almond/expected/beta.js: -------------------------------------------------------------------------------- 1 | define(["./sub/betaSub"],function(a){return{name:"beta",subName:a.name}}) -------------------------------------------------------------------------------- /test/fixtures/rjs-almond/sample/alpha.js: -------------------------------------------------------------------------------- 1 | define(function(require, exports) { 2 | exports.name = 'alpha'; 3 | }); 4 | -------------------------------------------------------------------------------- /vendor/jpegtran-8d/jpegtran.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/vendor/jpegtran-8d/jpegtran.exe -------------------------------------------------------------------------------- /test/fixtures/img/expected/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/img/expected/1.png -------------------------------------------------------------------------------- /test/fixtures/img/expected/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/img/expected/2.png -------------------------------------------------------------------------------- /test/fixtures/img/expected/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/img/expected/3.png -------------------------------------------------------------------------------- /test/fixtures/img/expected/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/img/expected/4.png -------------------------------------------------------------------------------- /test/fixtures/img/expected/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/img/expected/5.jpg -------------------------------------------------------------------------------- /test/fixtures/img/expected/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/img/expected/6.jpg -------------------------------------------------------------------------------- /vendor/optipng-0.7.1-win32/optipng.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/vendor/optipng-0.7.1-win32/optipng.exe -------------------------------------------------------------------------------- /vendor/optipng-0.7.1-win32/doc/authors.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/vendor/optipng-0.7.1-win32/doc/authors.txt -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | test/fixtures/h5bp 3 | .test 4 | npm-debug.log 5 | tasks/init/h5bp/root 6 | docs/css 7 | docs/index.html 8 | docs/api 9 | -------------------------------------------------------------------------------- /vendor/optipng-0.7.1-win32/doc/optipng.man.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/vendor/optipng-0.7.1-win32/doc/optipng.man.pdf -------------------------------------------------------------------------------- /test/fixtures/init/expected/robots.txt: -------------------------------------------------------------------------------- 1 | # www.robotstxt.org/ 2 | # https://developers.google.com/webmasters/control-crawl-index/ 3 | 4 | User-agent: * 5 | -------------------------------------------------------------------------------- /test/fixtures/rjs/sample/beta.js: -------------------------------------------------------------------------------- 1 | define(['./sub/betaSub'], function (betaSub) { 2 | return { 3 | name: 'beta', 4 | subName: betaSub.name 5 | }; 6 | }); 7 | -------------------------------------------------------------------------------- /test/fixtures/rjs-almond/sample/beta.js: -------------------------------------------------------------------------------- 1 | define(['./sub/betaSub'], function (betaSub) { 2 | return { 3 | name: 'beta', 4 | subName: betaSub.name 5 | }; 6 | }); 7 | -------------------------------------------------------------------------------- /test/fixtures/init/expected/apple-touch-icon-114x114-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mklabs/node-build-script/HEAD/test/fixtures/init/expected/apple-touch-icon-114x114-precomposed.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/h5bp"] 2 | path = test/h5bp 3 | url = git://github.com/h5bp/html5-boilerplate.git 4 | [submodule "docs/wiki"] 5 | path = docs/wiki 6 | url = git://github.com/h5bp/node-build-script.wiki.git 7 | -------------------------------------------------------------------------------- /support/docs/pages.txt: -------------------------------------------------------------------------------- 1 | Home 2 | install 3 | overview 4 | configuration 5 | tasks 6 | clean 7 | concat 8 | connect 9 | css 10 | min 11 | mkdirs 12 | rev 13 | serve 14 | usemin 15 | dom 16 | link-plugin 17 | rev-plugin 18 | script-plugin 19 | test 20 | -------------------------------------------------------------------------------- /test/features/template.feature: -------------------------------------------------------------------------------- 1 | Feature: <%= feature %> 2 | As a <%= role %> 3 | I want to <%= action %> 4 | So that <%= benefit %> 5 | 6 | Scenario: <%= scenario %> 7 | Given <%= context %> 8 | When <%= event %> 9 | Then <%= outcome %> 10 | 11 | -------------------------------------------------------------------------------- /test/fixtures/rjs/expected/main.js: -------------------------------------------------------------------------------- 1 | define("alpha",["require","exports","module"],function(a,b){b.name="alpha"}),define("sub/betaSub",{name:"betaSubName"}),define("beta",["./sub/betaSub"],function(a){return{name:"beta",subName:a.name}}),require(["alpha","beta"],function(a,b){}),define("main",function(){}) -------------------------------------------------------------------------------- /test/features/docs.feature: -------------------------------------------------------------------------------- 1 | Feature: DOCS task 2 | As a build script user 3 | I want to be able to run the docs task 4 | So that I can see the docs task in action 5 | 6 | Scenario: docs task 7 | Given I run the "docs" task 8 | When the script ends 9 | Then it should not fail miserably 10 | 11 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # This project has been sunsetted. 2 | 3 | Currently we don't maintain this project anymore and would advice you to not use 4 | it anymore. 5 | 6 | Please check out [Yeoman](http://yeoman.io/) which helps you scaffolding your 7 | project. 8 | 9 | The old readme file is [still available](documentation.md) though. 10 | -------------------------------------------------------------------------------- /test/features/rjs.feature: -------------------------------------------------------------------------------- 1 | Feature: RJS task 2 | As a build script user 3 | I want to be able to run the rjs task 4 | So that I can see the rjs task in action 5 | 6 | Scenario: rjs task 7 | Given I run the "rjs" task 8 | When the script ends 9 | Then "test/js" should be the same as "test/fixtures/rjs/expected/" 10 | 11 | -------------------------------------------------------------------------------- /test/features/img.feature: -------------------------------------------------------------------------------- 1 | Feature: IMG task 2 | As a build script user 3 | I want to be able to run the img task 4 | So that I can see the img task in action 5 | 6 | Scenario: img task 7 | Given I run the "img" task 8 | When the script ends 9 | Then ".test/img" should be the same as "test/fixtures/img/expected/" 10 | 11 | -------------------------------------------------------------------------------- /test/features/tar.feature: -------------------------------------------------------------------------------- 1 | Feature: TAR task 2 | As a build script user 3 | I want to be able to run the tar task 4 | So that I can see the tar task in action 5 | 6 | Scenario: tar task 7 | Given I run the "tar" task 8 | When the script ends 9 | Then ".test/tar" should be the same as "test/fixtures/tar/expected/" 10 | 11 | -------------------------------------------------------------------------------- /test/features/init.feature: -------------------------------------------------------------------------------- 1 | Feature: INIT task 2 | As a build script user 3 | I want to be able to run the init task 4 | So that I can see the init task in action 5 | 6 | Scenario: init task 7 | Given I run the "init" task 8 | When the script ends 9 | Then ".test/init" should be the same as "test/fixtures/init/expected/" 10 | 11 | -------------------------------------------------------------------------------- /test/features/css.feature: -------------------------------------------------------------------------------- 1 | Feature: CSS task 2 | As a build script user 3 | I want to be able to run the css task 4 | So that I can see the css task in action 5 | 6 | Scenario: css task 7 | Given I run the "css" task 8 | When the script ends 9 | Then ".test/css/style.css" should be the same as "test/fixtures/css/style.css" 10 | 11 | -------------------------------------------------------------------------------- /test/features/default.feature: -------------------------------------------------------------------------------- 1 | Feature: Default task 2 | As a build script user 3 | I want to be able to run the default task 4 | So that I can see the whole tasks in action 5 | 6 | Scenario: Default task 7 | Given I run the "default" task 8 | When the build script ends 9 | Then the outcome should be "test/fixtures/default" 10 | 11 | -------------------------------------------------------------------------------- /test/features/html.feature: -------------------------------------------------------------------------------- 1 | Feature: HTML task 2 | As a build script user 3 | I want to be able to run the html task 4 | So that I can see the html task in action 5 | 6 | Scenario: html task 7 | Given I run the "html" task 8 | When the script ends 9 | Then "./test/index.html" should be the same as "test/fixtures/html/expected.html" 10 | 11 | -------------------------------------------------------------------------------- /test/fixtures/css/grunt.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path'); 3 | 4 | module.exports = function(grunt) { 5 | 6 | // 7 | // Grunt configuration 8 | // 9 | grunt.config.init({ 10 | 11 | css: { 12 | 'css/compressed.css': ['css/style.css'] 13 | } 14 | 15 | }); 16 | 17 | grunt.loadNpmTasks(path.join(__dirname, '..')); 18 | 19 | }; 20 | -------------------------------------------------------------------------------- /test/features/usemin.feature: -------------------------------------------------------------------------------- 1 | Feature: USEMIN task 2 | As a build script user 3 | I want to be able to run the usemin task 4 | So that I can see the usemin task in action 5 | 6 | Scenario: usemin task 7 | Given I run the "usemin" task 8 | When the script ends 9 | Then ".test/usemin.html" should be the same as "test/fixtures/usemin/index.html" 10 | 11 | -------------------------------------------------------------------------------- /lib/plugins/index.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs'), 3 | path = require('path'); 4 | 5 | var plugins = module.exports; 6 | 7 | fs.readdirSync(path.join(__dirname)).forEach(function(file) { 8 | var plugin = file.replace(path.extname(file), ''); 9 | if(plugin === 'index' || path.extname(file) !== '.js') return; 10 | plugins.__defineGetter__(plugin, function() { 11 | return require('./' + plugin); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /h5bp.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var h5bp = module.exports; 4 | 5 | // hoist up any plugins onto the plugin object as lazy-loaded getters. 6 | h5bp.plugins = require('./lib/plugins'); 7 | 8 | // 9 | // custom package utilities, works in tandem with `grunt.utils`. 10 | // 11 | // Utils is there to package and provide a 12 | // 13 | // Will be merged into grunt.utils for further usage in tasks and helpers 14 | // 15 | h5bp.utils = require('./lib/utils'); 16 | -------------------------------------------------------------------------------- /support/docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "mklabs", 3 | "name": "grunt-doc-generator", 4 | "description": "Internal tool for generating api docs", 5 | "version": "0.0.0", 6 | "engines": { 7 | "node": "0.6.x" 8 | }, 9 | "dependencies": { 10 | "h5bp-docs": "http://nodeload.github.com/mklabs/h5bp-docs/tarball/next", 11 | "docco": "~0.3.0", 12 | "fstream": "~0.1.18" 13 | }, 14 | "bin": "./generate.js" 15 | } 16 | -------------------------------------------------------------------------------- /test/fixtures/css/app.css: -------------------------------------------------------------------------------- 1 | 2 | /* ============================================================================= 3 | App specific CSS file. 4 | 5 | This is usually where the site/app's CSS specific rules are setup. 6 | 7 | ========================================================================== */ 8 | 9 | 10 | @import url('ui/widget.css'); 11 | 12 | body:before { 13 | content: ' ☺ H E L L O ☺ '; 14 | font: 10em Helvetica, sans-serif; 15 | color: hotPink; 16 | } 17 | 18 | .css-reved .img-6 { background: url('../img/6.jpg'); } 19 | -------------------------------------------------------------------------------- /tasks/util.js: -------------------------------------------------------------------------------- 1 | 2 | var util = require('util'); 3 | 4 | // 5 | // This file defines a few utilities helpers. 6 | // 7 | // - inspect: takes an object, util.inspet it (with colorized output, 8 | // http://nodejs.org/api/util.html#util_util_inspect_object_showhidden_depth_colors). 9 | // 10 | 11 | module.exports = function(grunt) { 12 | // Output some info on given object, using util.inspect 13 | grunt.registerHelper('inspect', function(o) { 14 | var lf = grunt.utils.linefeed; 15 | grunt.log.ok(util.inspect(o, false, 4, true)); 16 | return grunt; 17 | }); 18 | }; 19 | 20 | -------------------------------------------------------------------------------- /test/features/steps/default.js: -------------------------------------------------------------------------------- 1 | 2 | Given(/I run the '(\w+)' task/, function() { 3 | // runt the $1 task 4 | helpers.run('$1', done); 5 | }); 6 | 7 | When(/the script ends/, function(done) { 8 | // not doing anything particularly usefull in this step but the hook is here 9 | // if we need to 10 | done(); 11 | }); 12 | 13 | Then(/'(.+)' should be the same as '(.+)'/, function() { 14 | helpers.assertFile('$1', '$2'); 15 | done(); 16 | }); 17 | 18 | Then(/'(.+)' dir should be the same as '(.+)'/, function() { 19 | helpers.assertFile('$1', '$2'); 20 | done(); 21 | }); 22 | -------------------------------------------------------------------------------- /test/fixtures/rjs/expected/plugins.js: -------------------------------------------------------------------------------- 1 | window.log=function a(){log.history=log.history||[],log.history.push(arguments);if(this.console){var b=arguments,c;try{b.callee=a.caller}catch(d){}c=[].slice.call(b),typeof console.log=="object"?log.apply.call(console.log,console,c):console.log.apply(console,c)}},function(a){function b(){}for(var c="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","),d;!!(d=c.pop());)a[d]=a[d]||b}(function(){try{return console.log(),window.console}catch(a){return window.console={}}}()) -------------------------------------------------------------------------------- /test/fixtures/usemin/tmpl.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 |
19 | 20 | -------------------------------------------------------------------------------- /test/fixtures/rjs-almond/expected/plugins.js: -------------------------------------------------------------------------------- 1 | window.log=function a(){log.history=log.history||[],log.history.push(arguments);if(this.console){var b=arguments,c;try{b.callee=a.caller}catch(d){}c=[].slice.call(b),typeof console.log=="object"?log.apply.call(console.log,console,c):console.log.apply(console,c)}},function(a){function b(){}for(var c="assert,count,debug,dir,dirxml,error,exception,group,groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,time,timeEnd,trace,warn".split(","),d;!!(d=c.pop());)a[d]=a[d]||b}(function(){try{return console.log(),window.console}catch(a){return window.console={}}}()) -------------------------------------------------------------------------------- /test/fixtures/usemin/tmpl.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 |
19 | 20 | -------------------------------------------------------------------------------- /test/fixtures/css/style.css: -------------------------------------------------------------------------------- 1 | 2 | /* ============================================================================= 3 | CSS App imports. 4 | 5 | These imports are inlined and minified by the build script. If 6 | you don't intend to use a build script, do not use @import 7 | statements. Use a single style.css file with bootstrap's style first, 8 | then your specific CSS files. 9 | 10 | Following assumes an app.css to put specific CSS rules. 11 | 12 | ========================================================================== */ 13 | 14 | @import url('h5bp.css'); 15 | @import url('app.css'); 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/fixtures/usemin/expected/tmpl.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 |
19 | 20 | -------------------------------------------------------------------------------- /test/fixtures/usemin/expected/tmpl.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 |
19 | 20 | -------------------------------------------------------------------------------- /test/fixtures/init/expected/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "name", 3 | "description": "The best project ever.", 4 | "version": "0.1.0", 5 | "homepage": "https://github.com/h5bp/node-build-script", 6 | "author": {}, 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/h5bp/node-build-script.git" 10 | }, 11 | "licenses": [ 12 | { 13 | "type": "MIT", 14 | "url": "https://github.com/h5bp/node-build-script/blob/master/LICENSE-MIT" 15 | } 16 | ], 17 | "dependencies": { 18 | "node-build-script": "http://nodeload.github.com/h5bp/node-build-script/tarball/dev" 19 | }, 20 | "keywords": [] 21 | } -------------------------------------------------------------------------------- /test/fixtures/init/expected/.gitignore: -------------------------------------------------------------------------------- 1 | # Numerous always-ignore extensions 2 | *.diff 3 | *.err 4 | *.orig 5 | *.log 6 | *.rej 7 | *.swo 8 | *.swp 9 | *.vi 10 | *~ 11 | *.sass-cache 12 | 13 | # OS or Editor folders 14 | .DS_Store 15 | ._* 16 | Thumbs.db 17 | .cache 18 | .project 19 | .settings 20 | .tmproj 21 | nbproject 22 | *.sublime-project 23 | *.sublime-workspace 24 | 25 | # Dreamweaver added files 26 | _notes 27 | dwsync.xml 28 | 29 | # Komodo 30 | *.komodoproject 31 | .komodotools 32 | 33 | # Espresso 34 | *.esproj 35 | *.espressostorage 36 | 37 | # Rubinius 38 | *.rbc 39 | 40 | # Folders to ignore 41 | .hg 42 | .svn 43 | .CVS 44 | intermediate 45 | publish 46 | .idea 47 | 48 | # build script local files 49 | build/buildinfo.properties 50 | build/config/buildinfo.properties 51 | -------------------------------------------------------------------------------- /test/tasks/minify.js: -------------------------------------------------------------------------------- 1 | // 2 | // Mocha generated tests 3 | // 4 | 5 | var assert = require('assert'), 6 | helpers = require('../helpers'); 7 | 8 | describe("build:minify task", function() { 9 | 10 | before(helpers.before); 11 | 12 | describe("As a build script user I want to run the build:minify task", function() { 13 | describe("build:minify task", function() { 14 | it("Given I run the 'build:minify' task", function(done) { 15 | helpers.run('build:minify', done); 16 | }); 17 | 18 | describe("When the build script ends", function() { 19 | it("Then the outcome should be 'test/fixtures/default/build.minify.html'", function() { 20 | helpers.assertFile('.test/publish/index.html', 'test/fixtures/default/build.minify.html'); 21 | }); 22 | }); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /test/tasks/default.js: -------------------------------------------------------------------------------- 1 | // 2 | // Mocha generated tests 3 | // 4 | 5 | var assert = require('assert'), 6 | helpers = require('../helpers'); 7 | 8 | describe("Default task", function() { 9 | 10 | before(helpers.before); 11 | 12 | describe("As a build script user I want to be able to run the default task So that I can see the whole tasks in action", function() { 13 | describe("Default task", function() { 14 | it("Given I run the 'default' task", function(done) { 15 | helpers.run('default', done); 16 | }); 17 | 18 | describe("When the build script ends", function() { 19 | it("Then the outcome should be 'test/fixtures/default'", function() { 20 | helpers.assertFile('.test/publish/index.html', 'test/fixtures/default/expected.html'); 21 | }); 22 | }); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /support/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // 4 | // Consider adding this to your local .git/hooks/pre-commit. To 5 | // install (or update): 6 | // 7 | // cat scripts/pre-commit > .git/hooks/pre-commit 8 | // chmod +x .git/hooks/pre-commit 9 | // 10 | // 11 | // Does two thing before allowing the commit: 12 | // 13 | // * runs `grunt lint` 14 | // * runs `npm test` 15 | // 16 | 17 | var spawn = require('child_process').spawn; 18 | 19 | run('grunt lint').on('next', run.bind({}, 'npm test')); 20 | 21 | function run(args) { 22 | args = args.split(' '); 23 | var cmd = args.shift(); 24 | var ch = spawn(cmd, args); 25 | ch.stdout.pipe(process.stdout); 26 | ch.stderr.pipe(process.stderr); 27 | ch.on('exit', function (code) { 28 | if(code) process.exit(code); 29 | var end = !ch.emit('next'); 30 | if() 31 | }); 32 | return ch; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /tasks/init/templates/rjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "name", 3 | "description": "rjs default setup", 4 | "version": "0.1.0", 5 | "repository": "git://github.com/h5bp/node-build-script.git", 6 | "homepage": "https://github.com/h5bp/node-build-script", 7 | "licenses": [ 8 | "MIT" 9 | ], 10 | "author_name": "", 11 | "author_email": "", 12 | "author_url": "", 13 | "force_update": "y/N", 14 | "js_safe_name": "name", 15 | "git_user": "h5bp", 16 | "setup": { 17 | "layout": "D" 18 | }, 19 | "gruntfile": { 20 | "dom": true, 21 | "min_concat": false, 22 | "package_json": true, 23 | "staging": "intermediate", 24 | "output": "publish", 25 | "css_dir": "css", 26 | "js_dir": "js", 27 | "img_dir": "img", 28 | "require_js": "y", 29 | "test_task": "qunit", 30 | "file_name": "<%= pkg.name %>", 31 | "test_dir": "test", 32 | "jquery": true 33 | } 34 | } -------------------------------------------------------------------------------- /tasks/init/templates/defaults.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "name", 3 | "description": "The best project ever.", 4 | "version": "0.1.0", 5 | "repository": "git://github.com/h5bp/node-build-script.git", 6 | "homepage": "https://github.com/h5bp/node-build-script", 7 | "licenses": [ 8 | "MIT" 9 | ], 10 | "author_name": "", 11 | "author_email": "", 12 | "author_url": "", 13 | "force_update": "y/N", 14 | "js_safe_name": "name", 15 | "git_user": "h5bp", 16 | "setup": { 17 | "layout": "D" 18 | }, 19 | "gruntfile": { 20 | "dom": true, 21 | "min_concat": true, 22 | "package_json": true, 23 | "staging": "intermediate", 24 | "output": "publish", 25 | "css_dir": "css", 26 | "js_dir": "js", 27 | "img_dir": "img", 28 | "require_js": "y/N", 29 | "test_task": "qunit", 30 | "file_name": "<%= pkg.name %>", 31 | "test_dir": "test", 32 | "jquery": true 33 | } 34 | } -------------------------------------------------------------------------------- /test/fixtures/init/expected/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 19 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /tasks/init/h5bp/prompts.txt: -------------------------------------------------------------------------------- 1 | 2 | [?] Do you need to force an update on master repo? (y/N) | force_update 3 | [?] Which layout ? ([D]efault, [C]ustom, [S]illy) | layout 4 | » [D]efault: Standard html5-boilerplate layout 5 | 6 | » [C]ustom: Specific build of html5-boilerplate. A series of prompt allows 7 | explicit file rewrites for common files and directories. 8 | 9 | » [S]illy: A "silly" build will prompt for every file in the h5bp repository. 10 | 11 | [?] CSS directory (css/) | css/ 12 | » You can change here the basic layout of the project you're about to 13 | create. The defaults values will create a standars h5bp project, but you 14 | might want to go for something different like: 15 | 16 | js/ → javascripts/ 17 | css/ → stylesheets/ 18 | img/ → images/ 19 | 20 | [?] Image directory (img/) | img/ 21 | [?] JavaScript directory (js/) | js/ 22 | 23 | -------------------------------------------------------------------------------- /tasks/init/h5bp/notes.txt: -------------------------------------------------------------------------------- 1 | First time the init task is run with `h5bp` template, the latest version 2 | of html5-boilerplate is downloaded locally. 3 | 4 | The resulting output depends on the few question grunt asks during the 5 | process. This includes some path rewrites (like renaming the 6 | `css/style.css` file to something else). 7 | 8 | Simply keep pressing enter for the default h5bp layout. 9 | 10 | There's four "step" of prompts: 11 | 12 | - project: Basic project configuration (name, description, version, ...) 13 | - layout: [D]efault, [C]ustom, [S]illy 14 | - files: Depends on layout value, series of prompts for specific 15 | file / directory renames 16 | - gruntfile: Gruntfile creation 17 | 18 | This template tries to guess file and directory paths, but you will most likely 19 | need to edit the generated grunt.js file before running grunt. If you run grunt 20 | after generating grunt.js, and grunt exits with errors, edit the grunt.js file! 21 | 22 | -------------------------------------------------------------------------------- /test/tasks/test-docs.js: -------------------------------------------------------------------------------- 1 | // 2 | // Mocha generated tests 3 | // 4 | 5 | var assert = require('assert'), 6 | helpers = require('../helpers'); 7 | 8 | describe("DOCS task", function() { 9 | 10 | describe("As a build script user I want to be able to run the docs task So that I can see the docs task in action", function() { 11 | 12 | describe("docs task", function() { 13 | 14 | it("Given I run the 'docs' task", function(done) { 15 | // runt the docs task 16 | helpers.run('docs', done); 17 | }); 18 | 19 | it("When the script ends", function(done) { 20 | // not doing anything particularly usefull in this step but the hook is here 21 | // if we need to 22 | done(); 23 | }); 24 | 25 | describe("Then it should not fail miserably", function() { 26 | it("(and you should see the documention in a web browser by now..)", function() { 27 | assert.ok(true); 28 | }); 29 | }); 30 | 31 | }); 32 | 33 | }); 34 | 35 | }); 36 | -------------------------------------------------------------------------------- /support/grunt-docs.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path'); 3 | 4 | // 5 | // This file defines a few development tasks (not part of the plugin 6 | // functionnality). 7 | // 8 | // - gendocs: doc generation for the project. Uses docco and a custom wrapper 9 | // to h5bp-docs to generate from project wiki (submodule). All the docs are all 10 | // compiled into one page (index.html). Docco is also used and generates from 11 | // files in `src/` into `api/`. 12 | // 13 | 14 | module.exports = function(grunt) { 15 | 16 | grunt.registerTask('gendocs', 'Generates docs/index.html from wiki pages', function() { 17 | var cb = this.async(); 18 | 19 | var gendoc = grunt.utils.spawn({ 20 | cmd: 'grunt', opts: { cwd: path.join(__dirname, 'docs') } 21 | }, function() {}); 22 | 23 | gendoc.stdout.pipe(process.stdout); 24 | gendoc.stderr.pipe(process.stderr); 25 | gendoc.on('exit', function(code) { 26 | if(code) grunt.warn('Something bad happend', code); 27 | cb(); 28 | }); 29 | }); 30 | 31 | }; 32 | 33 | -------------------------------------------------------------------------------- /test/tasks/test-html.js: -------------------------------------------------------------------------------- 1 | // 2 | // Mocha generated tests 3 | // 4 | 5 | var helpers = require('../helpers'); 6 | 7 | describe("HTML task", function() { 8 | 9 | // prepares the build dir 10 | before(helpers.before); 11 | 12 | describe("As a build script user I want to be able to run the html task So that I can see the html task in action", function() { 13 | 14 | describe("html task", function() { 15 | 16 | it("Given I run the 'html' task", function(done) { 17 | // runt the html task 18 | helpers.run('html', done); 19 | }); 20 | 21 | it("When the script ends", function(done) { 22 | // not doing anything particularly usefull in this step but the hook is here 23 | // if we need to 24 | done(); 25 | }); 26 | 27 | it("Then './test/index.html' should be the same as 'test/fixtures/html/index.html'", function(done) { 28 | helpers.assertFile('.test/index.html', 'test/fixtures/html/index.html'); 29 | done(); 30 | }); 31 | 32 | }); 33 | 34 | }); 35 | 36 | }); 37 | -------------------------------------------------------------------------------- /support/grunt-inspect.js: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // This file defines a few development tasks (not part of the plugin 4 | // functionnality). 5 | // 6 | // - list-helpers: run grunt list-helpers to get a list of all available 7 | // helpers in this current grunt instance (built-in plus user defined ones) 8 | // 9 | // - list-task: same here but for tasks. 10 | // 11 | 12 | module.exports = function(grunt) { 13 | 14 | grunt.registerTask('list-helpers', 'List all grunt registered helpers', function(helper) { 15 | var ls = grunt.log.wordlist(Object.keys(grunt.task._helpers), grunt.utils.linefeed); 16 | if(!helper) return grunt.log.ok(ls); 17 | grunt.log.subhead(helper + ' source:').ok(grunt.task._helpers[helper]); 18 | }); 19 | 20 | grunt.registerTask('list-task', 'List all grunt registered tasks', function(t) { 21 | var ls = grunt.log.wordlist(Object.keys(grunt.task._tasks), grunt.utils.linefeed); 22 | if(!t) return grunt.log.ok(ls); 23 | grunt.log.subhead(t + ' source:'); 24 | grunt.helper('inspect', grunt.task._tasks[t]); 25 | }); 26 | 27 | }; 28 | 29 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) HTML5 Boilerplate 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/optipng-0.7.1-win32/doc/license.txt: -------------------------------------------------------------------------------- 1 | 2 | Copyright (C) 2001-2012 Cosmin Truta and the Contributing Authors. 3 | For the purpose of copyright and licensing, the list of Contributing 4 | Authors is available in the accompanying AUTHORS file. 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the author(s) be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 19 | 2. Altered source versions must be plainly marked as such, and must not 20 | be misrepresented as being the original software. 21 | 22 | 3. This notice may not be removed or altered from any source distribution. 23 | 24 | -------------------------------------------------------------------------------- /test/fixtures/init/expected/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Paul Irish, Divya Manian", 3 | "name": "node-build-script", 4 | "description": "HTML5 Boilerplate node build script - Professional front-end template. So much goodness baked in by default", 5 | "version": "0.2.3", 6 | "homepage": "http://h5bp.com", 7 | "bin": { 8 | "html5-boilerplate": "./bin/h5bp", 9 | "h5bp": "./bin/h5bp" 10 | }, 11 | "main": "./h5bp", 12 | "dependencies": { 13 | "rimraf": "~2.0.1", 14 | "mkdirp": "~0.3.1", 15 | "ncp": "~0.2.6", 16 | "requirejs": "~1.0.8", 17 | "almond": "~0.1.1", 18 | "clean-css": "~0.3.2", 19 | "connect": "~1.8.6", 20 | "grunt": "~0.3.8", 21 | "socket.io": "~0.9.5", 22 | "request": "~2.9.202", 23 | "html-minifier": "~0.4.5", 24 | "which": "~1.0.5", 25 | "fstream": "~0.1.18", 26 | "fstream-ignore": "0.0.5" 27 | }, 28 | "devDependencies": { 29 | "mocha": "~1.0.3", 30 | "mocha-gherkin": "http://github.com/mklabs/mocha-gherkin/tarball/master" 31 | }, 32 | "scripts": { 33 | "pretest": "git submodule update --init", 34 | "test": "node node_modules/mocha/bin/mocha test/tasks/*.js --silent" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tasks/docs.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path'), 3 | exec = require('child_process').exec; 4 | 5 | var env = process.platform; 6 | 7 | module.exports = function(grunt) { 8 | 9 | grunt.registerTask('docs', 'grunt h5bp plugin documentation', function(target) { 10 | // path to the documentation page 11 | var docs = path.join(__dirname, '../docs/index.html'); 12 | 13 | // in case browser to use is setup in config or via --browser flag 14 | var gruntBro = grunt.config('browser') || grunt.option('browser') || ''; 15 | 16 | // async task 17 | var cb = this.async(); 18 | 19 | // depending on env.. try to guess the correct browser 20 | var browser = gruntBro ? gruntBro : 21 | env === 'win32' ? 'explorer' : 22 | env === 'darwin' ? 'open' : 23 | 'google-chrome'; 24 | 25 | /// XXX should which the browser, warn user if it fails and preven the 26 | // browser exec 27 | 28 | // spawn and forget, would probably ends up with no browser opening 29 | // but output to console webserver url. 30 | exec(browser + ' ' + docs, function(err) { 31 | if(err) { 32 | // XXX to be removed in grunt0.4.x 33 | grunt.log.error(err); 34 | return cb(false); 35 | } 36 | }); 37 | }); 38 | 39 | }; 40 | -------------------------------------------------------------------------------- /test/tasks/test-img.js: -------------------------------------------------------------------------------- 1 | // 2 | // Mocha generated tests 3 | // 4 | 5 | var helpers = require('../helpers'); 6 | 7 | describe("IMG task", function() { 8 | 9 | // mocha converter should expose hook for before, after, beforeEach and afterEach 10 | before(helpers.before); 11 | 12 | describe("As a build script user I want to be able to run the img task So that I can see the img task in action", function() { 13 | 14 | describe("img task", function() { 15 | 16 | it("Given I run the 'img' task", function(done) { 17 | // run the img task 18 | // 19 | // note: try commenting out this and execute done callback right away 20 | // to see mocha generate diff output on base64 encoded value in its 21 | // full glory 22 | helpers.run('img', done); 23 | }); 24 | 25 | it("When the script ends", function(done) { 26 | // not doing particularly usefull in this step 27 | // but the hook is here is we need to 28 | done(); 29 | }); 30 | 31 | it("Then '.test/img' dir should be the same as 'test/fixtures/img/expected/'", function(done) { 32 | helpers.assertDir('.test/img', 'test/fixtures/img/expected/'); 33 | done(); 34 | }); 35 | 36 | }); 37 | 38 | }); 39 | 40 | }); 41 | -------------------------------------------------------------------------------- /support/docs/readme.md: -------------------------------------------------------------------------------- 1 | 2 | node-build-script documentation site 3 | ==================================== 4 | 5 | Site is build with [h5bp-docs][] and generated from the project wiki. 6 | Each page may be rendered as individual page, but the docs are all compiled 7 | into one page (index.html). 8 | 9 | [Docco][] is also used and generates from files in `src/` into `api/`. 10 | 11 | The site can be built with: 12 | 13 | ``` sh 14 | $ grunt docs 15 | # >> docs copy 16 | $ open index.html 17 | ``` 18 | 19 | ```sh 20 | $ grunt wiki 21 | # >> docs:wiki copy 22 | open index.html 23 | ``` 24 | 25 | ```sh 26 | $ grunt api 27 | # >> docs:api 28 | $ ls api/ 29 | lib/ root/ tasks/ 30 | ``` 31 | 32 | Faster building & rebuilding 33 | ---------------------------- 34 | 35 | For quicker rebuilding of the site, you can start up the preview server: 36 | 37 | ``` sh 38 | $ grunt docs --serve # live rebuilding of the site! 39 | $ open http://localhost:3000 40 | ``` 41 | 42 | Update the docs 43 | --------------- 44 | 45 | The docs are managed through git submodules in `docs/` path. To update 46 | (or clone the first time it's run): 47 | 48 | ```sh 49 | $ git submodule update --init 50 | ``` 51 | 52 | [h5bp-docs]: https://github.com/mklabs/h5bp-docs 53 | [Docco]: http://jashkenas.github.com/docco/ 54 | -------------------------------------------------------------------------------- /bin/h5bp: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var fs = require('fs'), 3 | join = require('path').join, 4 | help = join(__dirname, 'help.txt'); 5 | 6 | // grunt with the plugin registered 7 | var grunt = require('grunt').npmTasks(join(__dirname, '../')); 8 | 9 | // Get back a reference to the internal grunt cli object (Yes, it's hacky) We 10 | // need this to be able to read grunt command line parsed options and tasks to 11 | // run to hook our internal additional logic. 12 | // 13 | // Another (nicer) alternative is to redo the command line parsing with nopt, 14 | // but it'll add yet another dependency for a very little thing 15 | var cli = require('grunt/lib/grunt/cli'); 16 | 17 | // command line options and remaining args 18 | var opts = cli.options, 19 | cmds = cli.tasks, 20 | route = cmds.join(' ').trim(''); 21 | 22 | // custom help, on `h5bp help` 23 | if(/^help/.test(route)) { 24 | if(/^help$/.test(route)) return fs.createReadStream(help).pipe(process.stdout); 25 | cli.tasks = cmds.join(':'); 26 | } 27 | 28 | // add the plugin version on `--version` 29 | if(opts.version) { 30 | console.log('h5bp v%s', require('../package.json').version); 31 | } 32 | 33 | // `h5bp init` -> `grunt init:h5bp` 34 | if(/^init$/.test(route)) { 35 | cli.tasks = 'init:h5bp'; 36 | } 37 | 38 | // the grunt cli 39 | grunt.cli(); 40 | -------------------------------------------------------------------------------- /bin/help.txt: -------------------------------------------------------------------------------- 1 | Usage: h5bp [options] [task [task ...]] 2 | 3 | Available tasks the h5bp plugin provides (for a full list, type h5bp --help 4 | 5 | clean Wipe the previous build dirs 6 | copy Copies the whole staging(intermediate/) folder to output 7 | (publish/) one 8 | css Concats, replaces @imports and minifies the CSS files * 9 | docs grunt h5bp plugin documentation 10 | dom dom-based build system 11 | html Basic to aggresive html minification 12 | img Optimizes .png/.jpg images using optipng/jpegtran 13 | mkdirs Prepares the build dirs * 14 | rev Automate the hash renames of assets filename * 15 | server Start a custom static web server 16 | usemin Replaces references to non-minified scripts / stylesheets * 17 | 18 | Build targets: h5bp build: 19 | 20 | default concat css min img rev usemin manifest 21 | text concat css min rev usemin manifest 22 | buildkit concat css min img rev usemin manifest html:buildkit 23 | basics concat css min img rev usemin manifest html:basics 24 | minify concat css min img rev usemin manifest html:compress 25 | 26 | For more information on the h5bp plugin, see https://github.com/h5bp/node-build-script/wiki 27 | 28 | For more information on grunt, see https://github.com/cowboy/grunt 29 | -------------------------------------------------------------------------------- /test/tasks/test-tar.js: -------------------------------------------------------------------------------- 1 | // 2 | // Mocha generated tests 3 | // 4 | 5 | var fs = require('fs'), 6 | path = require('path'), 7 | helpers = require('../helpers'); 8 | 9 | describe("TAR task", function() { 10 | 11 | // mocha converter should expose hook for before, after, beforeEach and afterEach 12 | // note: stop copy-pasting this everywhere, do it. 13 | before(helpers.before); 14 | 15 | describe("As a build script user I want to be able to run the tar task So that I can see the tar task in action", function() { 16 | 17 | describe("tar task", function() { 18 | 19 | it("Given I run the 'tar' task", function(done) { 20 | // runs the tar task 21 | // we gonna pack the whole '.test' dir in '.test/test.tar.gz' 22 | // curious to see how the task handle output within input.. 23 | helpers.run('tar --input ./ --output ./test.tgz', done); 24 | }); 25 | 26 | it("When the script ends", function(done) { 27 | // not doing particularly usefull in this step 28 | // but the hook is here is we need to 29 | done(); 30 | }); 31 | 32 | it("Then '.test/test.tgz' should be the same as 'test/fixtures/tar/test.tgz'", function(done) { 33 | helpers.assertLength('.test/test.tgz', 'test/fixtures/tar/test.tgz', done); 34 | }); 35 | 36 | }); 37 | 38 | }); 39 | 40 | }); 41 | -------------------------------------------------------------------------------- /test/fixtures/init/expected/humans.txt: -------------------------------------------------------------------------------- 1 | /* the humans responsible & colophon */ 2 | /* humanstxt.org */ 3 | 4 | 5 | /* TEAM */ 6 | : 7 | Site: 8 | Twitter: 9 | Location: 10 | 11 | /* THANKS */ 12 | Names (& URL): 13 | 14 | /* SITE */ 15 | Standards: HTML5, CSS3 16 | Components: Modernizr, jQuery 17 | Software: 18 | 19 | 20 | 21 | -o/- 22 | +oo//- 23 | :ooo+//: 24 | -ooooo///- 25 | /oooooo//: 26 | :ooooooo+//- 27 | -+oooooooo///- 28 | -://////////////+oooooooooo++////////////:: 29 | :+ooooooooooooooooooooooooooooooooooooo+:::- 30 | -/+ooooooooooooooooooooooooooooooo+/::////:- 31 | -:+oooooooooooooooooooooooooooo/::///////:- 32 | --/+ooooooooooooooooooooo+::://////:- 33 | -:+ooooooooooooooooo+:://////:-- 34 | /ooooooooooooooooo+//////:- 35 | -ooooooooooooooooooo////- 36 | /ooooooooo+oooooooooo//: 37 | :ooooooo+/::/+oooooooo+//- 38 | -oooooo/::///////+oooooo///- 39 | /ooo+::://////:---:/+oooo//: 40 | -o+/::///////:- -:/+o+//- 41 | :-:///////:- -:/:// 42 | -////:- --//: 43 | -- -: 44 | -------------------------------------------------------------------------------- /lib/support/reload.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | // 4 | // This is the client-side script that is "injected" automatically 5 | // with the connect (or reload) tasks. 6 | // 7 | // This is a first implementation. It may greatly be improved, the 8 | // grunt watch task has evolved and we might add some handy new 9 | // development utilities. 10 | // 11 | // We don't use jQuery for this, but one could totally change this (could we? 12 | // todo: add a way to overidde this file). It should work well on every (non 13 | // crappy) browsers. (eg. should implement querySelector). 14 | // 15 | 16 | var url = 'http://<%= hostname %>:<%= port %>', 17 | socket = io.connect(url); 18 | 19 | // watched files just changed, reload page. 20 | // also retrigger on reconnect event 21 | socket 22 | .on('changed', reload) 23 | .on('reconnect', reload) 24 | .on('error', function(errors) { 25 | var div = document.createElement('div'), 26 | errDiv = document.getElementById('h5bp-error'), 27 | first = document.body.querySelector('*'); 28 | 29 | div.innerHTML = errors.map(function(err) { 30 | return err.msg; 31 | }).join('
'); 32 | 33 | div.id = 'h5bp-error'; 34 | div.style.color = '#b94a48'; 35 | div.style.padding = '1em'; 36 | div.style.backgroundColor = '#f2dede'; 37 | div.style.borderColor = '#eed3d7'; 38 | 39 | if(errDiv) errDiv.innerHTML = div.innerHTML; 40 | else document.body.insertBefore(div, first); 41 | }); 42 | 43 | // Function.prototype.bind not always there 44 | function reload() { location.reload(); } 45 | })(); 46 | -------------------------------------------------------------------------------- /lib/utils/index.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path'); 3 | 4 | var utils = module.exports; 5 | 6 | // 7 | // flatiron/utile inspired. 8 | // 9 | // Set of common utilities, mainly defining wrapper to various utility modules 10 | // (like ncp, mkdir, rimraf) as lazy-loaded getters. 11 | // 12 | 13 | // 14 | // Wrapper to `require('mkdirp')` 15 | // 16 | utils.__defineGetter__('mkdirp', function () { 17 | return require('mkdirp'); 18 | }); 19 | 20 | // 21 | // Wrapper to `require('rimraf')` 22 | // 23 | utils.__defineGetter__('rimraf', function () { 24 | return require('rimraf'); 25 | }); 26 | 27 | // 28 | // Wrapper to `require('ncp').ncp` 29 | // 30 | utils.__defineGetter__('ncp', function () { 31 | return require('ncp').ncp; 32 | }); 33 | 34 | // 35 | // Wrapper to `require('./fetch')`, internal tarball helper using 36 | // mikeal/request, zlib and isaacs/tar. 37 | // 38 | utils.__defineGetter__('fetch', function () { 39 | return require('./fetch'); 40 | }); 41 | 42 | // **extend** a given object with the util module definition, taking care 43 | // of not triggering the getters. Doing so with _.extend makes the module 44 | // to be required right away. 45 | utils.extend = function extend(o) { 46 | var props = Object.keys(utils); 47 | props.forEach(function(prop) { 48 | o.__defineGetter__(prop, function() { 49 | return utils[prop]; 50 | }); 51 | }); 52 | return o; 53 | }; 54 | 55 | // 56 | // **join** basic wrapper to `path.join`, dealing with `//` and converting back 57 | // **`\` windows like path to unix like one. 58 | // 59 | utils.join = function join() { 60 | var args = Array.prototype.slice.call(arguments); 61 | return path.join.apply(path, args).replace(/\\/g, '/'); 62 | }; 63 | -------------------------------------------------------------------------------- /lib/utils/fetch.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | path = require('path'), 3 | zlib = require('zlib'), 4 | request = require('request'), 5 | tar = require('tar'); 6 | 7 | // Check if we're behind some kind of proxy. 8 | var proxy = process.env.http_proxy || process.env.HTTP_PROXY || 9 | process.env.https_proxy || process.env.HTTPS_PROXY || ''; 10 | 11 | // heavily based on npm's util/untar.js file 12 | module.exports = function fetch(tarball, target, cb) { 13 | var r = request.defaults({ proxy: proxy }), 14 | now = +new Date; 15 | 16 | var log = this.log 17 | .subhead('Fetching ' + tarball) 18 | .writeln('This might take a few moment'); 19 | 20 | // tarball untar opts 21 | var extractOpts = { type: 'Directory', path: target, strip: 1 }; 22 | 23 | // remote request --> zlib.Unzip() --> untar into h5bp/root 24 | var req = r.get(tarball).on('error', cb); 25 | 26 | req.on('data', function() { log.write('.'); }).on('end', function() { 27 | log.ok().writeln(); 28 | log.ok('Done in ' + (+new Date - now) / 1000 + 's.'); 29 | }); 30 | 31 | req 32 | // first gzip 33 | .pipe(zlib.Unzip()) 34 | .on('error', function(err) { 35 | console.error('unzip error', err); 36 | cb(err); 37 | }) 38 | // then tar extract into h5bp/root 39 | .pipe(tar.Extract(extractOpts)) 40 | .on('entry', function(entry) { 41 | log.write('.'); 42 | entry.props.uid = entry.uid = 501; 43 | entry.props.gid = entry.gid = 20; 44 | }) 45 | .on('error', function(err) { 46 | console.error('untar error', err); 47 | cb(err); 48 | }) 49 | .on('close', function() { 50 | log.writeln().ok('Done in ' + extractOpts.path).writeln(); 51 | cb(); 52 | }); 53 | }; 54 | -------------------------------------------------------------------------------- /test/tasks/test-css.js: -------------------------------------------------------------------------------- 1 | // 2 | // Mocha generated tests 3 | // 4 | 5 | var fs = require('fs'), 6 | path = require('path'), 7 | helpers = require('../helpers'); 8 | 9 | describe("CSS task", function() { 10 | 11 | // prepares the build dir 12 | before(helpers.before); 13 | 14 | before(function(done) { 15 | // copy in some files, with @imports to test out the inline imports 16 | var files = fs.readdirSync(path.join(__dirname, '../fixtures/css')) 17 | .filter(function(f) { 18 | return !(/expected/).test(f) && path.extname(f) === '.css'; 19 | }) 20 | .map(function(f) { 21 | return path.join('css', f); 22 | }); 23 | 24 | // gruntfile with our css config for test 25 | helpers.copyFile('test/fixtures/css/grunt.js', '.test/grunt.js', function(err) { 26 | if(err) return done(err); 27 | helpers.copy(files, '.test/css', done); 28 | }); 29 | 30 | }); 31 | 32 | describe("As a build script user I want to be able to run the css task So that I can see the css task in action", function() { 33 | 34 | describe("css task", function() { 35 | 36 | it("Given I run the 'css' task", function(done) { 37 | // runt the css task 38 | helpers.run('css', done); 39 | }); 40 | 41 | it("When the script ends", function(done) { 42 | // not doing particularly usefull in this step 43 | // but the hook is here is we need to 44 | done(); 45 | }); 46 | 47 | it("Then './test/css/compressed.css' should be the same as 'test/fixtures/css/expected.css'", function(done) { 48 | helpers.assertFile('.test/css/compressed.css', 'test/fixtures/css/expected.css'); 49 | done(); 50 | }); 51 | 52 | }); 53 | 54 | }); 55 | 56 | }); 57 | -------------------------------------------------------------------------------- /vendor/optipng-0.7.1-win32/doc/todo.txt: -------------------------------------------------------------------------------- 1 | 2 | OptiPNG - TO-DO list 3 | ==================== 4 | 5 | - Compression improvements: 6 | Use zlib's deflateTune(). 7 | Use 7zip's powerful deflation engine. 8 | (This is not possible with libpng, so a custom encoder is needed.) 9 | 10 | - Speed improvements: 11 | Avoid repeated filtering when trying a filter value more than once. 12 | (This is not possible with libpng, so a custom encoder is needed.) 13 | 14 | - Text chunk optimization: 15 | Smart selection between tEXt and zTXt. Similarly for iTXt. 16 | 17 | - More file recovery features. 18 | 19 | - Input from stdin; output to stdout. 20 | 21 | - Optimization of an entire directory, with and without subdirectory 22 | recursion: 23 | optipng dir/ 24 | optipng -recurse dir/ 25 | 26 | - Improved support for reading external image formats 27 | (e.g. compressed TIFF). 28 | 29 | - Support for conversion to a desired bit depth and color type: 30 | optipng -b16 -c6 ... 31 | 32 | - Support for handling metadata, e.g.: 33 | optipng -set tEXt=, # add or update chunk 34 | optipng -set sRGB=0 # add or update chunk 35 | optipng -set image.precision= # set the precision of all samples 36 | optipng -set image.alpha.precision= # set the precision of alpha samples 37 | optipng -reset image.alpha # make the image fully opaque 38 | optipng -strip hIST,sPLT # strip hIST and sPLT 39 | optipng -strip all -protect iCCP # strip all metadata except iCCP 40 | 41 | - Parallelization on multi-processor/multi-core machines. 42 | 43 | - A shared library (e.g. optipng.dll), to facilitate the development of 44 | PNG-optimizing GUI applications and plugins. 45 | 46 | -------------------------------------------------------------------------------- /tasks/rev.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | var fs = require('fs'), 4 | path = require('path'), 5 | crypto = require('crypto'); 6 | 7 | 8 | module.exports = function(grunt) { 9 | // rev task - reving is done in the `output/` directory 10 | grunt.registerMultiTask('rev', 'Automate the hash renames of assets filename', function() { 11 | grunt.helper('hash', this.data); 12 | }); 13 | 14 | // **hash** helper takes care of files revving, by renaming any files 15 | // in the given `files` pattern(s), with passed in `options`. 16 | // 17 | // - files - String or Array of glob pattern 18 | // - options - (optional) An Hash object where: 19 | // - cwd - Base directory to work from, glob patterns are 20 | // prepended to this path. 21 | // 22 | grunt.registerHelper('hash', function(files, opts) { 23 | opts = opts || {}; 24 | 25 | files = Array.isArray(files) ? files : [files]; 26 | grunt.file.expand(files).forEach(function(f) { 27 | var md5 = grunt.helper('md5', f), 28 | renamed = [md5.slice(0, 8), path.basename(f)].join('.'); 29 | 30 | grunt.verbose.ok().ok(md5); 31 | // create the new file 32 | fs.renameSync(f, path.resolve(path.dirname(f), renamed)); 33 | grunt.log.write(f + ' ').ok(renamed); 34 | }); 35 | }); 36 | 37 | 38 | // **md5** helper is a basic wrapper around crypto.createHash, with 39 | // given `algorithm` and `encoding`. Both are optional and defaults to 40 | // `md5` and `hex` values. 41 | grunt.registerHelper('md5', function(filepath, algorithm, encoding) { 42 | algorithm = algorithm || 'md5'; 43 | encoding = encoding || 'hex'; 44 | var hash = crypto.createHash(algorithm); 45 | hash.update(grunt.file.read(filepath)); 46 | grunt.log.verbose.write('Hashing ' + filepath + '...'); 47 | return hash.digest(encoding); 48 | }); 49 | }; 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /tasks/rjs.js: -------------------------------------------------------------------------------- 1 | var path = require('path'), 2 | rjs = require('requirejs'); 3 | 4 | module.exports = function(grunt) { 5 | grunt.task.registerTask('rjs', 'Optimizes javascript that actually is built with requirejs.', function () { 6 | this.requiresConfig(this.name); 7 | grunt.helper('rjs:optimize:js', grunt.config(this.name), this.async()); 8 | }); 9 | 10 | grunt.registerHelper('rjs:optimize:js', function(options, cb) { 11 | if(!cb) { cb = options; options = {}; } 12 | grunt.log.subhead('Options:'); 13 | grunt.helper('inspect', options); 14 | 15 | var originals = options.modules.map(function(m) { 16 | return { 17 | name: m.name, 18 | body: grunt.file.read(path.join(options.baseUrl, options.appDir, m.name + '.js')) 19 | }; 20 | }); 21 | 22 | if (options.almond === true) { 23 | grunt.log.ok('Including almond.js'); 24 | // resolve almond path 25 | if (typeof options.paths === 'undefined') { 26 | options.paths = {}; 27 | } 28 | options.paths.almond = require.resolve('almond').replace('.js', ''); 29 | // include almond to each module 30 | options.modules.forEach(function (module, idx) { 31 | grunt.verbose.ok('Adding almond to module: ' + module.name); 32 | // append almond to includes 33 | if (module.include) { 34 | module.include.push('almond'); 35 | } else { 36 | module.include = ['almond']; 37 | } 38 | }); 39 | } 40 | 41 | rjs.optimize(options, function() { 42 | originals.forEach(function(m) { 43 | var filepath = path.join(options.dir, m.name + '.js'); 44 | grunt.log 45 | .writeln('rjs optimized module ' + m.name) 46 | .writeln('>> ' + filepath); 47 | grunt.helper('min_max_info', grunt.file.read(filepath), m.body); 48 | }); 49 | 50 | cb(); 51 | }); 52 | }); 53 | 54 | }; 55 | -------------------------------------------------------------------------------- /test/tasks/test-init.js: -------------------------------------------------------------------------------- 1 | // 2 | // Mocha generated tests 3 | // 4 | 5 | var path = require('path'), 6 | helpers = require('../helpers'); 7 | 8 | describe("INIT task", function() { 9 | 10 | before(function(done) { 11 | helpers.setup({ nocopy: true }, done); 12 | }); 13 | 14 | describe("As a build script user I want to be able to run the init task So that I can see this in action", function() { 15 | 16 | 17 | describe("init task default template", function() { 18 | 19 | it("Given I run the 'init' task with default template", function(done) { 20 | // runt the init task 21 | var grunt = helpers.run('init --template defaults', { 22 | bin: path.join(__dirname, '../../bin/h5bp') 23 | }, done); 24 | 25 | }); 26 | 27 | it("When the script ends", function(done) { 28 | // not doing anything particularly usefull in this step but the hook is here 29 | // if we need to 30 | done(); 31 | }); 32 | 33 | it("Then '.test/' should be the same as 'test/fixtures/init/expected/'", function(done) { 34 | helpers.assertDir('.test/', 'test/fixtures/init/expected/'); 35 | done(); 36 | }); 37 | 38 | }); 39 | 40 | describe("init task rjs template", function() { 41 | 42 | it("Given I run the 'init' task with rjs template", function(done) { 43 | // runt the init task 44 | var grunt = helpers.run('init --template rjs --force', { 45 | bin: path.join(__dirname, '../../bin/h5bp') 46 | }, done); 47 | 48 | }); 49 | 50 | describe("When the script ends", function(done) { 51 | it("Then '.test/gruntfile.js' should be the same as 'test/fixtures/init/rjs.gruntfile.js'", function(done) { 52 | helpers.assertFile('.test/grunt.js', 'test/fixtures/init/rjs.gruntfile.js'); 53 | done(); 54 | }); 55 | }); 56 | 57 | }); 58 | }); 59 | 60 | }); 61 | -------------------------------------------------------------------------------- /test/fixtures/init/expected/apple-touch-icon.png: -------------------------------------------------------------------------------- 1 | �PNG 2 |  3 | IHDR99��s�PLTE�Y,Fgp# ! %"#(% $"0JQ"!%"�W)"#'$#  #! $!" $!!!%#!%)%�c1"&$Eem�d1�d2#'%2-&*+&�X))8:'/./HN�X)"&#�c7)6880(�d32LS�S6�c20IO�J*+77>X^`@0�jA-CH�W78.&[>/.=>�Q47/'�[,�a1,@E�U6�f8(44�d7+*%�^0+=@�Z-3MT\9(=V\�L*�c8-+&Z9(-BF&.,DclZ>. �\.$*(�e2�Z-[9(oE0'33'*&Ffo,;=�f4#)'�l?!&#"'$4LS&.+Dck#(&`@/3MS�F'wG0\?/�T2zH0l='�J(�M(�S14GJ,=?6KN�W3�O)�W*%,)Ddm;/&bA00?@)89�N,2-%�W,EfnE4)zA'.@B[8&-;;�L*�R(�K*�E'X=.�M+;SX/GM(56mD/�W3,?C�\.b:':QV.FK7LO/@C2CF70(A3*.<=B`g)54F2&1CF/ADQ5&�X2V<.'22�b1�b0-;=�R)�`/�a/�a01@B1FIW7&$*'$)'4-&&11�Y*�Y*%,+&.-1BD�\,�Y*�Z+�[,�\,�[+E5*�V)=2)�]-�Z+*;>w@'�^-�^.+;<')%�T)�pD�_.�I(�_.-::�[-4FI>]e7KO�`/3DG�N,�W3�g5�e3�X*&/.$,*B1&5.&*661KRoD/�W18OS�T�.���,�$P�� |`d]�rw���!J8���&�)@%B!Y��d9J�0�Ƀ�%B +�I&�1��� 0c+��A��8��C��� / 5 | t�9 ��8�Jj���j�0e"���!�4�:�f 6 | lk�\QDkwD0� 7 | p��s�C���TQ�J�j�A%�1U�b�-A�XUc`�R�Z�%"��9�  Ӝ���ll儉(��-�m ������� cm(|��$�)-�T���ڶ�5�80�2ԫ 8 | ���z�%����-��-��χ�C�%�̽,4��R f CW��j+p���Rڰ&�B��z:Ƽ��*f(��6G ;c팶J���� � 9 | ��$�2���hy�ٺv똟1n^�M�H�����j� jӚ���M��5N4�B^4%�RWpӬ���Y�N��T͉�:7%�R̪��WUU�o%�+���D��4� 10 | 5�Jwy�gSϘ�ʠ�.��8�Y�䕚�8��r����޼�ҟ4�4)C\��zEr��\���� 11 | ��������wV�� eb��F�e��;�&;�;��{�o�3� 12 | %V�Z����>m�b���y�H�x��{���n�_� P��22ߠֳ��w-j=�@��o�Bxk�9�~�k=k���G2�˵���٢1���՞tƒ���N���"��ɵ��(���c�� A9O{����g����������`�mN�N|®��}؟Z����c��i�6\F�� ��k��:�ٳN]0��͝ZN�3�;�y�����J�(8�����/���tX��R�~�$�;G�g=�qi��~̄V?�:���Sg��%{^}%�+�q��������>���>����sH�}?vww�_��5P/�ޣ�7�+�>q�^�~{����9���gj�)Ϸ�_�� 13 | �V�����g���`Z�(ڌ�����`�+�x�^�������kr�q���y�w�s�=���K�B���?0�'λL�?',���s��l:�ď �p 14 | w�C]�um�D ]H�;�lqO���eL�q?��;��P��l�E�z�M�߇��������5��o�ߩ������P������� n<IEND�B`� -------------------------------------------------------------------------------- /test/fixtures/init/expected/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- 1 | �PNG 2 |  3 | IHDR99��s�PLTE�Y,Fgp# ! %"#(% $"0JQ"!%"�W)"#'$#  #! $!" $!!!%#!%)%�c1"&$Eem�d1�d2#'%2-&*+&�X))8:'/./HN�X)"&#�c7)6880(�d32LS�S6�c20IO�J*+77>X^`@0�jA-CH�W78.&[>/.=>�Q47/'�[,�a1,@E�U6�f8(44�d7+*%�^0+=@�Z-3MT\9(=V\�L*�c8-+&Z9(-BF&.,DclZ>. �\.$*(�e2�Z-[9(oE0'33'*&Ffo,;=�f4#)'�l?!&#"'$4LS&.+Dck#(&`@/3MS�F'wG0\?/�T2zH0l='�J(�M(�S14GJ,=?6KN�W3�O)�W*%,)Ddm;/&bA00?@)89�N,2-%�W,EfnE4)zA'.@B[8&-;;�L*�R(�K*�E'X=.�M+;SX/GM(56mD/�W3,?C�\.b:':QV.FK7LO/@C2CF70(A3*.<=B`g)54F2&1CF/ADQ5&�X2V<.'22�b1�b0-;=�R)�`/�a/�a01@B1FIW7&$*'$)'4-&&11�Y*�Y*%,+&.-1BD�\,�Y*�Z+�[,�\,�[+E5*�V)=2)�]-�Z+*;>w@'�^-�^.+;<')%�T)�pD�_.�I(�_.-::�[-4FI>]e7KO�`/3DG�N,�W3�g5�e3�X*&/.$,*B1&5.&*661KRoD/�W18OS�T�.���,�$P�� |`d]�rw���!J8���&�)@%B!Y��d9J�0�Ƀ�%B +�I&�1��� 0c+��A��8��C��� / 5 | t�9 ��8�Jj���j�0e"���!�4�:�f 6 | lk�\QDkwD0� 7 | p��s�C���TQ�J�j�A%�1U�b�-A�XUc`�R�Z�%"��9�  Ӝ���ll儉(��-�m ������� cm(|��$�)-�T���ڶ�5�80�2ԫ 8 | ���z�%����-��-��χ�C�%�̽,4��R f CW��j+p���Rڰ&�B��z:Ƽ��*f(��6G ;c팶J���� � 9 | ��$�2���hy�ٺv똟1n^�M�H�����j� jӚ���M��5N4�B^4%�RWpӬ���Y�N��T͉�:7%�R̪��WUU�o%�+���D��4� 10 | 5�Jwy�gSϘ�ʠ�.��8�Y�䕚�8��r����޼�ҟ4�4)C\��zEr��\���� 11 | ��������wV�� eb��F�e��;�&;�;��{�o�3� 12 | %V�Z����>m�b���y�H�x��{���n�_� P��22ߠֳ��w-j=�@��o�Bxk�9�~�k=k���G2�˵���٢1���՞tƒ���N���"��ɵ��(���c�� A9O{����g����������`�mN�N|®��}؟Z����c��i�6\F�� ��k��:�ٳN]0��͝ZN�3�;�y�����J�(8�����/���tX��R�~�$�;G�g=�qi��~̄V?�:���Sg��%{^}%�+�q��������>���>����sH�}?vww�_��5P/�ޣ�7�+�>q�^�~{����9���gj�)Ϸ�_�� 13 | �V�����g���`Z�(ڌ�����`�+�x�^�������kr�q���y�w�s�=���K�B���?0�'λL�?',���s��l:�ď �p 14 | w�C]�um�D ]H�;�lqO���eL�q?��;��P��l�E�z�M�߇��������5��o�ߩ������P������� n<IEND�B`� -------------------------------------------------------------------------------- /test/fixtures/init/expected/apple-touch-icon-57x57-precomposed.png: -------------------------------------------------------------------------------- 1 | �PNG 2 |  3 | IHDR99��s�PLTE�Y,Fgp# ! %"#(% $"0JQ"!%"�W)"#'$#  #! $!" $!!!%#!%)%�c1"&$Eem�d1�d2#'%2-&*+&�X))8:'/./HN�X)"&#�c7)6880(�d32LS�S6�c20IO�J*+77>X^`@0�jA-CH�W78.&[>/.=>�Q47/'�[,�a1,@E�U6�f8(44�d7+*%�^0+=@�Z-3MT\9(=V\�L*�c8-+&Z9(-BF&.,DclZ>. �\.$*(�e2�Z-[9(oE0'33'*&Ffo,;=�f4#)'�l?!&#"'$4LS&.+Dck#(&`@/3MS�F'wG0\?/�T2zH0l='�J(�M(�S14GJ,=?6KN�W3�O)�W*%,)Ddm;/&bA00?@)89�N,2-%�W,EfnE4)zA'.@B[8&-;;�L*�R(�K*�E'X=.�M+;SX/GM(56mD/�W3,?C�\.b:':QV.FK7LO/@C2CF70(A3*.<=B`g)54F2&1CF/ADQ5&�X2V<.'22�b1�b0-;=�R)�`/�a/�a01@B1FIW7&$*'$)'4-&&11�Y*�Y*%,+&.-1BD�\,�Y*�Z+�[,�\,�[+E5*�V)=2)�]-�Z+*;>w@'�^-�^.+;<')%�T)�pD�_.�I(�_.-::�[-4FI>]e7KO�`/3DG�N,�W3�g5�e3�X*&/.$,*B1&5.&*661KRoD/�W18OS�T�.���,�$P�� |`d]�rw���!J8���&�)@%B!Y��d9J�0�Ƀ�%B +�I&�1��� 0c+��A��8��C��� / 5 | t�9 ��8�Jj���j�0e"���!�4�:�f 6 | lk�\QDkwD0� 7 | p��s�C���TQ�J�j�A%�1U�b�-A�XUc`�R�Z�%"��9�  Ӝ���ll儉(��-�m ������� cm(|��$�)-�T���ڶ�5�80�2ԫ 8 | ���z�%����-��-��χ�C�%�̽,4��R f CW��j+p���Rڰ&�B��z:Ƽ��*f(��6G ;c팶J���� � 9 | ��$�2���hy�ٺv똟1n^�M�H�����j� jӚ���M��5N4�B^4%�RWpӬ���Y�N��T͉�:7%�R̪��WUU�o%�+���D��4� 10 | 5�Jwy�gSϘ�ʠ�.��8�Y�䕚�8��r����޼�ҟ4�4)C\��zEr��\���� 11 | ��������wV�� eb��F�e��;�&;�;��{�o�3� 12 | %V�Z����>m�b���y�H�x��{���n�_� P��22ߠֳ��w-j=�@��o�Bxk�9�~�k=k���G2�˵���٢1���՞tƒ���N���"��ɵ��(���c�� A9O{����g����������`�mN�N|®��}؟Z����c��i�6\F�� ��k��:�ٳN]0��͝ZN�3�;�y�����J�(8�����/���tX��R�~�$�;G�g=�qi��~̄V?�:���Sg��%{^}%�+�q��������>���>����sH�}?vww�_��5P/�ޣ�7�+�>q�^�~{����9���gj�)Ϸ�_�� 13 | �V�����g���`Z�(ڌ�����`�+�x�^�������kr�q���y�w�s�=���K�B���?0�'λL�?',���s��l:�ď �p 14 | w�C]�um�D ]H�;�lqO���eL�q?��;��P��l�E�z�M�߇��������5��o�ߩ������P������� n<IEND�B`� -------------------------------------------------------------------------------- /test/tasks/test-rjs.js: -------------------------------------------------------------------------------- 1 | // 2 | // Mocha generated tests 3 | // 4 | 5 | var helpers = require('../helpers'); 6 | 7 | var describeTask = function(taskName, fixturePath) { 8 | 9 | describe(taskName, function() { 10 | 11 | before(function(done) { 12 | helpers.before(function(err) { 13 | if(err) return done(err); 14 | 15 | helpers.copyFile(fixturePath + 'grunt.js', '.test/grunt.js', function(err) { 16 | if(err) return done(err); 17 | var files = [ 18 | 'rjs/sample/alpha.js', 19 | 'rjs/sample/beta.js', 20 | 'rjs/sample/main.js' 21 | ]; 22 | 23 | helpers.copy(files, '.test/js', function(err) { 24 | if(err) return done(err); 25 | helpers.copyFile(fixturePath + 'sample/sub/betaSub.js', '.test/js/sub/betaSub.js', done); 26 | }); 27 | }); 28 | }); 29 | }); 30 | 31 | describe("As a build script user I want to be able to run the rjs task So that I can see the rjs task in action", function() { 32 | 33 | describe("rjs task", function() { 34 | 35 | // XXX the rjs task takes up to 8s on my machine with almost nothing to 36 | // process (sample files are really basic...) 37 | // figure out what's going wrong 38 | it("Given I run the 'rjs' task", function(done) { 39 | // runt the rjs task 40 | helpers.run('rjs', done); 41 | }); 42 | 43 | it("When the script ends", function(done) { 44 | // remove build.txt as it contains system-dependent almond path 45 | helpers.rmFile('.test/js/build.txt', function(err) { 46 | if (err) return done(err); 47 | return done(); 48 | }); 49 | }); 50 | 51 | it("Then '.test/js' should be the same as " + fixturePath + "expected/'", function(done) { 52 | helpers.assertDir('.test/js', fixturePath + 'expected/'); 53 | done(); 54 | }); 55 | 56 | }); 57 | 58 | }); 59 | 60 | }); 61 | 62 | }; 63 | 64 | describeTask("RJS task", 'test/fixtures/rjs/'); 65 | describeTask("RJS task (almond)", 'test/fixtures/rjs-almond/'); 66 | -------------------------------------------------------------------------------- /docs/api/lib/docs/img.html: -------------------------------------------------------------------------------- 1 | img.js -------------------------------------------------------------------------------- /docs/api/lib/docs/rev.html: -------------------------------------------------------------------------------- 1 | rev.js -------------------------------------------------------------------------------- /test/fixtures/init/expected/apple-touch-icon-72x72-precomposed.png: -------------------------------------------------------------------------------- 1 | �PNG 2 |  3 | IHDRHHb3Cu�PLTE#(%"!%#! $!"!Fgp0JQ!!%""&$�e2D5+.EJ$)(?Z`5-&�d2*66)*%#)&#)'>1&+=@�P+�Y,�I((67=X^)79 $"#  �W)�W) # *9;/FL#!" 3FH*671FJ1GL#'%#(&$!g<'�g5*:<&/.�e4�f4,?C>3*@\c,88-::91)�d3a;((21%)%�e2Eem�[-�N)�L)�U)�X*J3&(56�Z*�[,')%(0/�Y*�S)S6&|J39PT-@D�P(�mC�C(B1&�O4,AE�^09QW&0/r@))323MS�\-4.')44>]f4OV�V*'u@'&.-'113HK�P5;SX�^7:QV�_2/>?�c1�d1�c1�[6;TY5HK�nB9/&�c1�b0�P3�b0uH3�a0�b0�a/�`/�M+(10e;'�X*-CH�X)zJ3�Y*,*%�\,�[+�U3�M4�Z+�]-�Z+�f5�]-�^-�`8�T4-;;�^.�pD�c9�_.�nA�\,,;=�_.�_.�`/'44mE1lD0�a8$+*5PXd@-�M4uE0Y=.8MQ�O+*;=zB(d@/'33�d8qE0A3*/HOiB/�P,�V2x@'�L*70(\9(�]1�F'I7,& ���IDATx^��c�s=��[sl۶_۶m�m۶m۶m{}'8�m��̏�3��Z�:{��������_\Si)��-˵g�w������m�� ~�7���������m�]Vf+�T����ge8� 8�i #)�� �$�>� �b�iv �,I"�)�x������VS���s���Q�U I�sfIb Nir:�555��}i�,C��4��H'�q�S�pH���X�I�&� '��1N�����r�Q>@AR��Dx>�W� ��0���r"�_r|pJ����,��P��G 渍�&a�jR�Ulj3�A$Ri��9�'8#���%Q���$H�B����q[��E U��X q{>��)�A�YΌ�ұJS���p����%��Fle�y>�#]QԤ�&zR��$���2��~8q��� q�Ǔ26  A�XL�WA�!g�^8ym��㙒�%A�� 4 | p;�����_���B�8����� 5 | �g�ă�~ ��� C�7ϵ4T�����9�Ǫr�U �� 6 | ���5$+�xLU]D����L������f��5$��`=��xI�,8JA��Аt�(_���5F��ȴ�X�-���aID�;�r�'71�7��:'J"�X��M<��G� ��TgK"u7&���B�so�* Sr;H�#rA��;���μ���$��H%�[h��3�5�Tx#���8 h�}(��k\,�@:���{Q7�3oc�"�ft���4�~��q�䍈���{V���0u[�?W}t���d�M��Ȉ�Y���_�����z�No<$b��m��~��up� 7 | �0�FҠVr��W�^�M��^�-�<$'5� ^�[}i����ӓC%���BAtw�����ԡsz:�!��u�i�ˑU2�s���ܓi�_ ���I� �3��� ԩS��頿C�1AjFX��wT���"� ��Ђ���NIƆ�D9��Ԧ}���=_��ٱ��vJ�l��|Uw];��,A|e��~��}�Ӫ�͘���M' 28 | }, 29 | 30 | concat: { 31 | dist: { 32 | src: ['js/plugins.js', 'js/script.js'], 33 | dest: 'js/scripts.js' 34 | } 35 | }, 36 | 37 | min: { 38 | dist: { 39 | src: ['js/scripts.js'], 40 | dest: 'js/scripts.min.js' 41 | } 42 | }, 43 | 44 | css: { 45 | 'css/style.css': ['css/style.css'] 46 | }, 47 | 48 | rev: { 49 | js: 'js/**/*.js', 50 | css: 'css/**/*.css', 51 | img: 'img/**/*' 52 | }, 53 | 54 | img: { 55 | dist: '' 56 | }, 57 | 58 | usemin: { 59 | html: ['**/*.html', '**/*.mustache', '**/*.hbs'], 60 | css: ['**/*style.css'] 61 | }, 62 | 63 | html: { 64 | files: ['**/*.html'] 65 | }, 66 | 67 | serve: { 68 | staging: { port: 3000 }, 69 | output: { port: 3001 } 70 | }, 71 | 72 | dom: { 73 | 74 | files : ['*.html'], 75 | 76 | options: { 77 | // 78 | // cwd - base directory to work from. 79 | // out - optimized assets get resolved to this path. 80 | // 81 | }, 82 | 83 | 'script[data-build]' : h5bp.plugins.script, 84 | 'link' : h5bp.plugins.link, 85 | 'img' : h5bp.plugins.img, 86 | 'script, link, img' : h5bp.plugins.rev 87 | } 88 | }); 89 | 90 | 91 | grunt.loadNpmTasks(path.join(__dirname, '..')); 92 | 93 | }; 94 | -------------------------------------------------------------------------------- /test/fixtures/init/expected/readme.md: -------------------------------------------------------------------------------- 1 | # [HTML5 Boilerplate](http://html5boilerplate.com) 2 | 3 | HTML5 Boilerplate is a professional front-end template that helps you build fast, robust, adaptable, and future-proof websites. Spend more time developing and less time reinventing the wheel. 4 | 5 | This project is the product of many years of iterative development and combined community knowledge. It does not impose a specific development philosophy or framework, so you're free to architect your code in the way that you want. 6 | 7 | 8 | ## Quick start 9 | 10 | Clone the git repo - `git clone git://github.com/h5bp/html5-boilerplate.git` - or [download it](https://github.com/h5bp/html5-boilerplate/zipball/master) 11 | 12 | 13 | ## Features 14 | 15 | * HTML5 ready. Use the new elements with confidence. 16 | * Cross-browser compatible (Chrome, Opera, Safari, Firefox 3.6+, IE6+). 17 | * Designed with progressive enhancement in mind. 18 | * CSS normalizations and common bug fixes. 19 | * IE-specific classes for easier cross-browser control. 20 | * A default print stylesheet, performance optimized. 21 | * Mobile browser optimizations. 22 | * Protection against any stray `console.log` causing JavaScript errors in IE6/7. 23 | * The latest jQuery via CDN, with a local fallback. 24 | * A custom Modernizr build for feature detection. 25 | * An optimized Google Analytics snippet. 26 | * Apache server caching, compression, and other configuration defaults for Grade-A performance. 27 | * Cross-domain Ajax and Flash. 28 | * "Delete-key friendly." Easy to strip out parts you don't need. 29 | * Extensive inline and accompanying documentation. 30 | 31 | 32 | ## Contributing 33 | 34 | Anyone and everyone is welcome to [contribute](https://github.com/h5bp/html5-boilerplate/wiki/contribute). Hundreds of developers have helped make the HTML5 Boilerplate what it is today. 35 | 36 | 37 | ## Project information 38 | 39 | * Source: http://github.com/h5bp/html5-boilerplate 40 | * Web: http://html5boilerplate.com 41 | * Docs: http://html5boilerplate.com/docs 42 | * Twitter: http://twitter.com/h5bp 43 | 44 | 45 | ## License 46 | 47 | ### Major components: 48 | 49 | * jQuery: MIT/GPL license 50 | * Modernizr: MIT/BSD license 51 | * Normalize.css: Public Domain 52 | 53 | ### Everything else: 54 | 55 | The Unlicense (aka: public domain) 56 | -------------------------------------------------------------------------------- /lib/plugins/link.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs'), 3 | path = require('path'), 4 | rjs = require('requirejs'); 5 | 6 | var plugin = module.exports; 7 | 8 | // give it a name 9 | plugin.name = 'link'; 10 | 11 | // give it some defaults 12 | plugin.defaults = { 13 | dir: process.cwd(), 14 | output: 'css/style.min.css' 15 | }; 16 | 17 | // and the main plugin handler, mixed in jsdom's jquery as $.fn.pluginame 18 | plugin.handler = function link($, options, cb) { 19 | options = options || {}; 20 | 21 | // hmm, will probably do this in other way, log object from grunt is passed 22 | // in options, or anything that comply to the grunt logger api. Using 23 | // `writeln` as of now. Will probably work out a better adapter system. 24 | var log = options.log || { writeln: $.noop }; 25 | 26 | // reg cache 27 | var rAbs = /\/\//; 28 | 29 | // don't act on zero-element 30 | if(!this.length) { 31 | cb(); 32 | return this; 33 | } 34 | 35 | // don't handle external files 36 | var set = this.filter(function(i, el) { 37 | var src = $(el).attr('href'); 38 | return !rAbs.test(src); 39 | }); 40 | 41 | // size of the passed in collection, after absolute url filter 42 | // used to emit `success` on last iteration. 43 | var ln = set.length; 44 | log.writeln(' › Link plugin - about to process ' + set.length + ' element(s)'); 45 | 46 | return set.each(function(i, target) { 47 | var el = $(this), 48 | last = ln === (i + 1), 49 | src = el.attr('href'), 50 | file = path.resolve(options.cwd, src); 51 | 52 | if(!path.existsSync(file)) return cb(new Error('no ' + src)); 53 | 54 | var href = el.data('build') || options.output, 55 | output = path.join(options.out, href); 56 | 57 | // have rjs deal with import inlines, plus file writes 58 | var config = options.rjs || options.requirejs || { 59 | optimizeCss: 'standard.keepLines' 60 | }; 61 | config.out = output; 62 | config.cssIn = file; 63 | 64 | log.writeln(' › Handle: ' + el.get(0).outerHTML); 65 | log.writeln((' › Writing optimized CSS file to output ' + output).bold); 66 | rjs.optimize(config, function(res) { 67 | if(!last) return el.remove(); 68 | // update dom tree accordingly 69 | el.attr('href', href); 70 | cb(null, res); 71 | }); 72 | }); 73 | }; 74 | 75 | -------------------------------------------------------------------------------- /test/fixtures/rjs-almond/expected/main.js: -------------------------------------------------------------------------------- 1 | var requirejs,require,define;(function(a){function i(a,b){var c=b&&b.split("/"),e=d.map,f=e&&e["*"]||{},g,h,i,j,k,l,m;if(a&&a.charAt(0)==="."&&b){c=c.slice(0,c.length-1),a=c.concat(a.split("/"));for(k=0;m=a[k];k++)if(m===".")a.splice(k,1),k-=1;else if(m===".."){if(k===1&&(a[2]===".."||a[0]===".."))return!0;k>0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}if((c||f)&&e){g=a.split("/");for(k=g.length;k>0;k-=1){h=g.slice(0,k).join("/");if(c)for(l=c.length;l>0;l-=1){i=e[c.slice(0,l).join("/")];if(i){i=i[h];if(i){j=i;break}}}j=j||f[h];if(j){g.splice(0,k,j),a=g.join("/");break}}}return a}function j(b,c){return function(){return h.apply(a,f.call(arguments,0).concat([b,c]))}}function k(a){return function(b){return i(b,a)}}function l(a){return function(c){b[a]=c}}function m(d){if(c.hasOwnProperty(d)){var f=c[d];delete c[d],e[d]=!0,g.apply(a,f)}if(!b.hasOwnProperty(d))throw new Error("No "+d);return b[d]}function n(a,b){var c,d,e=a.indexOf("!");return e!==-1?(c=i(a.slice(0,e),b),a=a.slice(e+1),d=m(c),d&&d.normalize?a=d.normalize(a,k(b)):a=i(a,b)):a=i(a,b),{f:c?c+"!"+a:a,n:a,p:d}}function o(a){return function(){return d&&d.config&&d.config[a]||{}}}var b={},c={},d={},e={},f=[].slice,g,h;g=function(d,f,g,h){var i=[],k,p,q,r,s,t;h=h||d;if(typeof g=="function"){f=!f.length&&g.length?["require","exports","module"]:f;for(t=0;t
-------------------------------------------------------------------------------- /test/fixtures/default/build.minify.html: -------------------------------------------------------------------------------- 1 |
-------------------------------------------------------------------------------- /test/fixtures/init/expected/favicon.ico: -------------------------------------------------------------------------------- 1 | (6h^ h�( ���������������������������`0`f`ffff0Fff@0wfgvf`F@���������������������( &*"*F�*:^>>.*Z�&2>66*:j�*.2.2*.^�&**RN:*.&*R�*F~FF2:f�2b�&*&.BfBB22^�.:JFr�.b�:Z�6N�.Z�*6F::.6j�*2622*.^�..&2JzJF6**&.BjFB2:^�*********************** (**' % '*   2 | 3 | 4 |  5 | 6 | 7 | % #$***) *** ********************���������������������(  %(#'%(#�%(#�%(#�%(#�%(#�%(#�%(#'%(#r%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#r%(#%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#%(#�%(#�%(#�;:-�RN8�(*$�%(#�%(#�%(#�')$�EC2�%(#�%(#�%(#�%(#-%(#�%(#�%(#�,Aj�%*)�IG4�02(�%(#�%*)�,Aj�66+�%(#�%(#�%(#�%(#-%(#�%(#�%(#�%(#�/Af�+R��&0=�FD2�&0=�+R��/Af�'*$�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#�&++�3_��+Y��(D�+Y��3_��&++�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#�*G��,\��,\��,\��*G��BA1�?=/�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#�(:_�-Z��.^��.^��.^��-Z��(:_�=<.�JG5�+-&�%(#�%(#�%(#�%(#�(.2�:Y��Dp��Dp��9g��/`��:i��Dp��Dp��:Y��(.2�-.'�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#�4N��0b��2I{�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#-%(#�%(#�%(#�%(#�%(#�,8K�7h��+5D�%(#�%(#�%(#�%(#�%(#�%(#�%(#*%(#�%(#�%(#�%(#�%(#�%(#�;^��/0(�%(#�%(#�%(#�%(#�%(#�%(#�%(#%(#�%(#�%(#�%(#�%(#�)07�%(#�%(#�%(#�%(#�%(#�%(#�%(#%(#r%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#�%(#r%(#'%(#�%(#�%(#�%(#�%(#�%(#�%(#'������������������� -------------------------------------------------------------------------------- /test/fixtures/init/expected/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 28 | 29 | 30 |
31 |

Not found :(

32 |

Sorry, but the page you were trying to view does not exist.

33 |

It looks like this was the result of either:

34 |
    35 |
  • a mistyped address
  • 36 |
  • an out-of-date link
  • 37 |
38 | 41 | 42 |
-------------------------------------------------------------------------------- /test/fixtures/rjs/grunt.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | module.exports = function(grunt) { 3 | 4 | // Project configuration. 5 | grunt.initConfig({ 6 | // the staging directory used during the process 7 | staging: 'intermediate', 8 | // final build output 9 | output: 'publish', 10 | // filter any files matching one of the below pattern during mkdirs task 11 | // the pattern in the .gitignore file should work too. 12 | exclude: '.git* build/** node_modules/** grunt.js package.json *.md'.split(' '), 13 | mkdirs: { 14 | staging: '' 15 | }, 16 | // concat css/**/*.css files, inline @import, output a single minified css 17 | css: { 18 | 'css/style.css': ['css/**/*.css'] 19 | }, 20 | // Renames JS/CSS to prepend a hash of their contents for easier 21 | // versioning 22 | rev: { 23 | js: 'js/**/*.js', 24 | css: 'css/**/*.css', 25 | img: 'img/**' 26 | }, 27 | // update references in html to revved files 28 | usemin: { 29 | files: ['**/*.html'] 30 | }, 31 | // html minification 32 | html: '', 33 | // Optimizes JPGs and PNGs (with jpegtran & optipng) 34 | img: { 35 | dist: '' 36 | }, 37 | watch: { 38 | files: '', 39 | tasks: 'lint qunit' 40 | }, 41 | lint: { 42 | files: ['grunt.js', 'js/**/*.js', 'test/**/*.js'] 43 | }, 44 | qunit: { 45 | files: ['test/**/*.html'] 46 | }, 47 | jshint: { 48 | options: { 49 | curly: true, 50 | eqeqeq: true, 51 | immed: true, 52 | latedef: true, 53 | newcap: true, 54 | noarg: true, 55 | sub: true, 56 | undef: true, 57 | boss: true, 58 | eqnull: true, 59 | browser: true 60 | }, 61 | globals: { 62 | jQuery: true 63 | } 64 | }, 65 | rjs: { 66 | modules: [{ 67 | name: 'main', 68 | }], 69 | dir: 'js', 70 | appDir: 'js', 71 | baseUrl: './', 72 | pragmas: { 73 | doExclude: true 74 | }, 75 | skipModuleInsertion: false, 76 | optimizeAllPluginResources: true, 77 | findNestedDependencies: true 78 | } 79 | }); 80 | 81 | 82 | // in rjs setup, the concat and min task are overriden to use rjs optimizr 83 | grunt.renameTask('concat', '_concat').registerTask('concat', 'rjs (noop)', function() { 84 | grunt.log.writeln('the concat in rjs setup is a noop, rjs optimizer somewhat replace js concatenation'); 85 | }); 86 | grunt.renameTask('min', '_min').registerTask('min', 'rjs'); 87 | 88 | 89 | // uncomment this line if you're using the build script as a grunt plugin 90 | // it should be installed locally, even better if put in your package.json's 91 | // dependency 92 | // 93 | grunt.loadNpmTasks(require('path').join(__dirname, '../')); 94 | }; 95 | -------------------------------------------------------------------------------- /test/fixtures/rjs-almond/grunt.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | module.exports = function(grunt) { 3 | 4 | // Project configuration. 5 | grunt.initConfig({ 6 | // the staging directory used during the process 7 | staging: 'intermediate', 8 | // final build output 9 | output: 'publish', 10 | // filter any files matching one of the below pattern during mkdirs task 11 | // the pattern in the .gitignore file should work too. 12 | exclude: '.git* build/** node_modules/** grunt.js package.json *.md'.split(' '), 13 | mkdirs: { 14 | staging: '' 15 | }, 16 | // concat css/**/*.css files, inline @import, output a single minified css 17 | css: { 18 | 'css/style.css': ['css/**/*.css'] 19 | }, 20 | // Renames JS/CSS to prepend a hash of their contents for easier 21 | // versioning 22 | rev: { 23 | js: 'js/**/*.js', 24 | css: 'css/**/*.css', 25 | img: 'img/**' 26 | }, 27 | // update references in html to revved files 28 | usemin: { 29 | files: ['**/*.html'] 30 | }, 31 | // html minification 32 | html: '', 33 | // Optimizes JPGs and PNGs (with jpegtran & optipng) 34 | img: { 35 | dist: '' 36 | }, 37 | watch: { 38 | files: '', 39 | tasks: 'lint qunit' 40 | }, 41 | lint: { 42 | files: ['grunt.js', 'js/**/*.js', 'test/**/*.js'] 43 | }, 44 | qunit: { 45 | files: ['test/**/*.html'] 46 | }, 47 | jshint: { 48 | options: { 49 | curly: true, 50 | eqeqeq: true, 51 | immed: true, 52 | latedef: true, 53 | newcap: true, 54 | noarg: true, 55 | sub: true, 56 | undef: true, 57 | boss: true, 58 | eqnull: true, 59 | browser: true 60 | }, 61 | globals: { 62 | jQuery: true 63 | } 64 | }, 65 | rjs: { 66 | almond: true, 67 | modules: [{ 68 | name: 'main', 69 | }], 70 | dir: 'js', 71 | appDir: 'js', 72 | baseUrl: './', 73 | pragmas: { 74 | doExclude: true 75 | }, 76 | skipModuleInsertion: false, 77 | optimizeAllPluginResources: true, 78 | findNestedDependencies: true 79 | } 80 | }); 81 | 82 | 83 | // in rjs setup, the concat and min task are overriden to use rjs optimizr 84 | grunt.renameTask('concat', '_concat').registerTask('concat', 'rjs (noop)', function() { 85 | grunt.log.writeln('the concat in rjs setup is a noop, rjs optimizer somewhat replace js concatenation'); 86 | }); 87 | grunt.renameTask('min', '_min').registerTask('min', 'rjs'); 88 | 89 | 90 | // uncomment this line if you're using the build script as a grunt plugin 91 | // it should be installed locally, even better if put in your package.json's 92 | // dependency 93 | // 94 | grunt.loadNpmTasks(require('path').join(__dirname, '../')); 95 | }; 96 | -------------------------------------------------------------------------------- /tasks/css.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs'), 3 | path = require('path'), 4 | cleanCSS = require('clean-css'), 5 | rjs = require('requirejs'); 6 | 7 | module.exports = function(grunt) { 8 | 9 | // **css* task works pretty much the same as grunt's min task. The task 10 | // target is the destination, data is an array of glob patterns. These 11 | // files are concataned and run through requirejs optimizer to handle 12 | // @import inlines in CSS files. 13 | grunt.task.registerMultiTask('css', 'Concats, replaces @imports and minifies the CSS files', function() { 14 | // css files for this subtarget destination 15 | var files = this.data; 16 | 17 | // subtarget name is the output destination 18 | var target = this.target; 19 | 20 | // async task 21 | var cb = this.async(); 22 | 23 | // write the destination css, a simple concat of the original files 24 | // without compression 25 | grunt.file.write(target, grunt.helper('mincss', files, { 26 | nocompress: true 27 | })); 28 | 29 | // replace @import statements 30 | // 31 | // XXX no error handling in this helper so far.. 32 | // Check that rjs returns an error when something wrong (if it throws...) 33 | // if it is bubble the error back here 34 | grunt.helper('rjs:optimize:css', target, function() { 35 | // do the minification once inline imports are done 36 | grunt.log.write('Writing css files to ' + target + '...'); 37 | grunt.file.write(target, grunt.helper('mincss', target)); 38 | grunt.log.ok(); 39 | cb(); 40 | }); 41 | }); 42 | 43 | // 44 | // **mincss** basic utility to concat CSS files and run them through 45 | // [cleanCSS](https://github.com/GoalSmashers/clean-css), might opt to use 46 | // [https://github.com/jzaefferer/grunt-css] plugin. 47 | // 48 | grunt.registerHelper('mincss', function(files, o) { 49 | o = o || {}; 50 | files = grunt.file.expandFiles(files); 51 | return files.map(function(filepath) { 52 | var content = grunt.file.read(filepath); 53 | return o.nocompress ? content : cleanCSS.process(content); 54 | }).join(''); 55 | }); 56 | 57 | // **rjs:optimize:css** is an helper using rjs to optimize a single file, 58 | // mainly to properly import multi-level of @import statements, which can be 59 | // tricky with all the url rewrites. 60 | // 61 | // file - Path to the css file to optimize 62 | // options - (optional) rjs configuration 63 | // cb - callback function to call on completion 64 | grunt.registerHelper('rjs:optimize:css', function(file, options, cb) { 65 | if(!cb) { cb = options; options = {}; } 66 | options.cssIn = file; 67 | options.out = options.out || file; 68 | options.optimizeCss = 'standard.keepComments.keepLines'; 69 | var before = grunt.file.read(file); 70 | rjs.optimize(options, function() { 71 | grunt.helper('min_max_info', grunt.file.read(file), before); 72 | cb(); 73 | }); 74 | }); 75 | 76 | 77 | }; 78 | 79 | -------------------------------------------------------------------------------- /test/fixtures/init/expected/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /grunt.js: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // Our project gruntfile 4 | // ===================== 5 | // 6 | // Type grunt --help to see the support tasks, or browse the support/grunt-*.js 7 | // files for further informations. 8 | // 9 | // Type grunt lint to run jshint through the sources. 10 | // 11 | // Type grunt test (or npm test) to run the default test suite (wrapper to Mocha) 12 | // 13 | // Type grunt test: to run the tests on a subset of files. 14 | // 15 | // Test 16 | // ---- 17 | // 18 | // Tests are stored in test/tasks/*.js. They run agains the files in test/h5bp 19 | // which is a submodule pointing to h5bp/html5-boilerplate repository. 20 | // 21 | // test/helpers/* include utility functions to run grunt in a spawned process 22 | // for a given task. 23 | // 24 | // test/features/*.feature are Gherkin feature file (cucumber) from which tests 25 | // can be autogenerated. These are not unit tests, and may be seen more as 26 | // functional tests. test/features/steps/*.js may contain step definitions, and 27 | // are used to bootstrap basic and shared codes / assertions throughout our 28 | // test suite. 29 | // 30 | // test/fixtures/* includes files that gets either copied before running a test, 31 | // or expected files that get compared to files when a task is complete. 32 | // Usually, they're stored within a folder mapping the related task's name. 33 | // 34 | // Type open test/index.html to see the generated documentation from our tests, 35 | // done using the really neat mocha documentation reporter. 36 | // 37 | // Unit tests for each helpers defined remains to be done. 38 | // 39 | 40 | module.exports = function(grunt) { 41 | 42 | // Project configuration. 43 | grunt.initConfig({ 44 | lint: { 45 | grunt: ['grunt.js', 'tasks/*.js'], 46 | lib: ['lib/plugins/*.js'] 47 | }, 48 | watch: { 49 | files: '', 50 | tasks: 'lint:grunt' 51 | }, 52 | jshint: { 53 | options: { 54 | es5: true, 55 | node: true, 56 | curly: false, 57 | eqeqeq: true, 58 | immed: true, 59 | latedef: false, 60 | newcap: true, 61 | noarg: true, 62 | sub: true, 63 | undef: true, 64 | boss: true, 65 | eqnull: true 66 | } 67 | }, 68 | test: { 69 | // each task is tested individually, with basic files comparison with 70 | // what is in test/fixtures/ 71 | tasks: ['test/tasks/test-*.js'], 72 | 73 | // default task with default options 74 | builds: ['test/tasks/default.js', 'test/tasks/usemin.js'] 75 | 76 | // css: 'test/tasks/test-css.js' 77 | // img: 'test/tasks/test-img.js' 78 | // tar: 'test/tasks/test-tar.js' 79 | // usemin: 'test/tasks/test-usemin.js' 80 | // docs: 'test/tasks/test-docs.js' 81 | // html: 'test/tasks/test-html.js' 82 | // init: 'test/tasks/test-init.js' 83 | } 84 | }); 85 | 86 | // Default task. 87 | grunt.registerTask('default', 'lint'); 88 | 89 | // load support for tasks, not part of the plugin functionnality. 90 | // This gruntfile is our dev gruntfile. 91 | grunt.loadTasks('./support/'); 92 | }; 93 | -------------------------------------------------------------------------------- /docs/api/tasks/docs/main.html: -------------------------------------------------------------------------------- 1 | main.js -------------------------------------------------------------------------------- /test/fixtures/init/rjs.gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | module.exports = function(grunt) { 3 | 4 | // Project configuration. 5 | grunt.initConfig({ 6 | // the staging directory used during the process 7 | staging: 'intermediate', 8 | // final build output 9 | output: 'publish', 10 | // filter any files matching one of the below pattern during mkdirs task 11 | // the pattern in the .gitignore file should work too. 12 | exclude: '.git* build/** node_modules/** grunt.js package.json *.md'.split(' '), 13 | mkdirs: { 14 | staging: '' 15 | }, 16 | // concat css/**/*.css files, inline @import, output a single minified css 17 | css: { 18 | 'css/style.css': ['css/**/*.css'] 19 | }, 20 | // Renames JS/CSS to prepend a hash of their contents for easier 21 | // versioning 22 | rev: { 23 | js: 'js/**/*.js', 24 | css: 'css/**/*.css', 25 | img: 'img/**' 26 | }, 27 | // update references in html to revved files 28 | usemin: { 29 | files: ['**/*.html'] 30 | }, 31 | // html minification 32 | html: '', 33 | // Optimizes JPGs and PNGs (with jpegtran & optipng) 34 | img: { 35 | dist: '' 36 | }, 37 | watch: { 38 | files: '', 39 | tasks: 'lint qunit' 40 | }, 41 | 42 | pkg: '', 43 | meta: { 44 | banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + 45 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' + 46 | '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' + 47 | '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + 48 | ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */' 49 | }, 50 | lint: { 51 | files: ['grunt.js', 'js/**/*.js', 'test/**/*.js'] 52 | }, 53 | qunit: { 54 | files: ['test/**/*.html'] 55 | }, 56 | jshint: { 57 | options: { 58 | curly: true, 59 | eqeqeq: true, 60 | immed: true, 61 | latedef: true, 62 | newcap: true, 63 | noarg: true, 64 | sub: true, 65 | undef: true, 66 | boss: true, 67 | eqnull: true, 68 | browser: true 69 | }, 70 | globals: { 71 | jQuery: true 72 | } 73 | }, 74 | rjs: { 75 | modules: [{ 76 | name: 'main', 77 | }], 78 | dir: 'publish/js', 79 | appDir: 'js', 80 | baseUrl: './', 81 | pragmas: { 82 | doExclude: true 83 | }, 84 | skipModuleInsertion: false, 85 | optimizeAllPluginResources: true, 86 | findNestedDependencies: true 87 | } 88 | }); 89 | 90 | 91 | // in rjs setup, the concat and min task are overriden to use rjs optimizr 92 | grunt.renameTask('concat', '_concat').registerTask('concat', 'rjs (noop)', function() { 93 | grunt.log.writeln('the concat in rjs setup is a noop, rjs optimizer somewhat replace js concatenation'); 94 | }); 95 | grunt.renameTask('min', '_min').registerTask('min', 'rjs'); 96 | 97 | 98 | // uncomment this line if you're using the build script as a grunt plugin 99 | // it should be installed locally, even better if put in your package.json's 100 | // dependency 101 | // 102 | // grunt.loadNpmTasks('node-build-script'); 103 | }; 104 | -------------------------------------------------------------------------------- /documentation.md: -------------------------------------------------------------------------------- 1 | The h5bp build script now has three versions, the main one being 2 | the [ant-build-script][]. This 3 | project is the node version, and it uses [Grunt][grunt] as a build tool. 4 | 5 | It is packaged as a [grunt plugin](https://github.com/gruntjs/grunt/wiki/Creating-plugins) and provides you a bunch of tasks and 6 | helpers to help improve the performance of your site/app in a production 7 | environment. 8 | 9 | ## Description 10 | 11 | This node/grunt-based build script tries to be as close as possible to 12 | the [ant-build-script][], a really great project you should check out. 13 | 14 | It is still in early stage of development, but will get better, thanks to [your feedback](https://github.com/h5bp/node-build-script/issues). 15 | 16 | ## Quick start 17 | 18 | **Fancy one line install:** 19 | 20 | ```sh 21 | # see the gist: https://gist.github.com/2359881 22 | $ curl https://raw.github.com/gist/2359881/install.sh | sh 23 | ``` 24 | 25 | **Slightly less fancier** 26 | 27 | ```sh 28 | $ npm install http://github.com/h5bp/node-build-script/tarball/master -g 29 | $ h5bp help 30 | $ h5bp init 31 | ``` 32 | 33 | **Developing locally** 34 | 35 | ```sh 36 | $ git clone git://github.com/h5bp/node-build-script.git 37 | $ cd node-build-script 38 | $ npm link 39 | ``` 40 | 41 | ## Features 42 | 43 | * Concats / Compresses JS 44 | * Concats / Compresses CSS 45 | * Inline CSS imports via RequireJS 46 | * Basic to aggressive html minification (via [html-minfier][]) 47 | * Optimizes JPGs and PNGs (with jpegtran & optipng) 48 | * Renames JS/CSS to prepend a hash of their contents for easier versioning 49 | * Revises the file names of your assets so that you can use heavy caching 50 | * Updates your HTML to reference these new hyper-optimized CSS + JS files 51 | * Experimental dom-based (with [JSDOM]()) build system. 52 | * May rerun the build script on file changes (grunt's watch task ❤) 53 | * May automatically reload the page in your browsers whenever watched files 54 | change, through some [socket.io] magic. 55 | 56 | ## Getting started 57 | 58 | * [Install](https://github.com/h5bp/node-build-script/wiki/install) the package 59 | * Check out the extensive [grunt documentation](https://github.com/gruntjs/grunt/blob/master/README.md), specifically the 60 | [Getting Started](https://github.com/gruntjs/grunt/wiki/Getting-started) section. 61 | * Learn more about [Usage](https://github.com/h5bp/node-build-script/wiki/overview) 62 | and [Configuration](https://github.com/h5bp/node-build-script/wiki/configuration) 63 | * Look at the [available tasks](https://github.com/h5bp/node-build-script/wiki/tasks) 64 | * Test out the experimental 65 | [dom-based](https://github.com/h5bp/node-build-script/wiki/dom) build 66 | system. 67 | 68 | ## Project information 69 | 70 | * Source: http://github.com/h5bp/node-build-script 71 | * Docs: http://github.com/h5bp/node-build-script/wiki 72 | * Issues: http://github.com/h5bp/node-build-script/issues 73 | 74 | ## Run the tests 75 | 76 | ```sh 77 | $ npm test 78 | ``` 79 | 80 | [grunt]: https://github.com/gruntjs/grunt 81 | [grunt documentation]: https://github.com/gruntjs/grunt/blob/master/docs/toc.md 82 | [grunt plugin]: https://github.com/gruntjs/grunt/blob/master/docs/plugins.md 83 | [Getting Started]: https://github.com/gruntjs/grunt/blob/master/docs/getting_started.md#readme) 84 | [JSDOM]: https://github.com/tmpvar/jsdom 85 | [ant-build-script]: https://github.com/h5bp/ant-build-script 86 | [socket.io]: http://socket.io 87 | [html-minifier]: https://github.com/kangax/html-minifier 88 | -------------------------------------------------------------------------------- /tasks/h5bp.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs'), 3 | join = require('path').join, 4 | util = require('util'), 5 | h5bp = require('../'); 6 | 7 | // 8 | // ant build script has a nice notion of environment, this defaults to 9 | // production. And we only support env=prod for now. 10 | // 11 | // not implemented tasks (add noop waithing for their impl): manifest images 12 | // 13 | // 14 | // 15 | // build (default) no html optimizations 16 | // 17 | // text same as build but without image (png/jpg) optimizing 18 | // 19 | // buildkit minor html optimizations, all html whitespace/comments 20 | // maintained 21 | // 22 | // basics same as build minus plugs minor html optimizations 23 | // (extra quotes and comments removed) 24 | // (todo: inline script/style minified) 25 | // 26 | // minify same as build plus full html minification, 27 | // 28 | 29 | module.exports = function(grunt) { 30 | 31 | // Setup some default alias... 32 | grunt.registerTask('default', 'build:default'); 33 | grunt.registerTask('reload', 'default connect watch:reload'); 34 | 35 | // XXX move targets to the gruntfile? easier for user to tweak build profiles 36 | var targets = { 37 | default: 'concat css min img rev usemin manifest', 38 | text: 'concat css min rev usemin manifest', 39 | buildkit: 'concat css min img rev usemin manifest html:buildkit', 40 | basics: 'concat css min img rev usemin manifest html:basics', 41 | minify: 'concat css min img rev usemin manifest html:compress' 42 | }; 43 | 44 | var targetList = grunt.log.wordlist(Object.keys(targets)); 45 | grunt.registerTask('build', 'Run a predefined target - build: \n' + targetList, function(target) { 46 | var valid = Object.keys(targets); 47 | if(!~valid.indexOf(target)) { 48 | grunt.log 49 | .error('Not a valid target') 50 | .error(grunt.helper('invalid targets', targets)); 51 | return false; 52 | } 53 | 54 | var tasks = ['intro clean mkdirs', targets[target], 'copy time'].join(' '); 55 | grunt.task.run(tasks); 56 | }); 57 | 58 | grunt.registerHelper('invalid targets', function(valid, code) { 59 | var msg = Object.keys(valid).map(function(key) { 60 | if(/pre|post/.test(key)) return ''; 61 | return grunt.helper('pad', key, 10) + '# '+ valid[key]; 62 | }).join(grunt.utils.linefeed); 63 | 64 | var err = new Error(grunt.utils.linefeed + msg); 65 | err.code = code || 3; 66 | return err; 67 | }); 68 | 69 | grunt.registerHelper('pad', function pad(str, max) { 70 | return str.length > max ? str : 71 | str + new Array(max - str.length + 1).join(' '); 72 | }); 73 | 74 | // To-be-implemented tasks 75 | grunt.registerTask('manifest', 'TBD - Generates appcache manifest file.', function() { 76 | grunt.log.error('not yet implemented'); 77 | }); 78 | 79 | var now = +new Date(); 80 | grunt.registerTask('time', 'Print sucess status with elapsed time', function() { 81 | grunt.log.ok('Build sucess. Done in ' + ((+new Date() - now) / 1000) + 's'); 82 | }); 83 | 84 | // Output some size info about a file, from a stat object. 85 | grunt.registerHelper('min_max_stat', function(min, max) { 86 | min = typeof min === 'string' ? fs.statSync(min) : min; 87 | max = typeof max === 'string' ? fs.statSync(max) : max; 88 | grunt.log.writeln('Uncompressed size: ' + String(max.size).green + ' bytes.'); 89 | grunt.log.writeln('Compressed size: ' + String(min.size).green + ' bytes minified.'); 90 | }); 91 | 92 | }; 93 | -------------------------------------------------------------------------------- /test/fixtures/init/expected/apple-touch-icon-144x144-precomposed.png: -------------------------------------------------------------------------------- 1 | �PNG 2 |  3 | IHDR��И��PLTER;-�E)�\3�Z6|B'�e8gC11@BEfo�e8�W1�mC�oD�U6�a8�e:�U2s@(zC).c:'R5&0?@�X2�L*tG2[>/�W6(45|J4F2&�X*�J*�C'\eT;-�W1j=(�R5Ffo�Q(/GM91(�^.+88n>'?0&�b85IL�i:qE0�T)�O)�f57.&b@/,?C�X+�U,Y_3MU,AE3FH/>?2CF/HN5.&�g5�[-2JP%.,�K(N9,�[,E6+�].-CH6MR**%.FL'330IO�V),>A(561BD�e3*9<0AC)8:)32&0/$,*.DJA]e3DG*66#(&0,&%)%1LS+;>B2&>X^�c1')%�W)(20zH1A3*=V\'/.'21�b0$*(�b0�d2�e2�a/�d1�a0�a/�X)�c1�W)�a0�c1�[+�Z+�Z*�b0�]-�X*�^-�^.�^.�pD�\,�X)�[,�Y*�[+�_.�\,�`/�^-�Z+�Y*�]-�_.�_.�`/Fgp0JQ! "&#"&$!%"!#'%#'$!%# %""'$" #!# !" $!$! $""!&## #(%��A� �IDATx^��ǒ�0P��� O{���zoN[�R*4�y#��Y��u���-��������4��~��튢���[;;�NOu}�\Vm�IR�%_}~�|d����YR����Jέ�'���n��}_r��ܧYK^�ί"���hO�8����jČ�oiT0ə9�‘J%2�G�#��3��5�@�% \�;����*)z�9�rPΈ���\ 4 | 5�Ē=�I=W�8(���9��j)̍cKD&� ���r���G>P����%�D������qY+��u¡���>?� Tє��ZJ�֍ �ԔAd����z*p���i S&�*U)�����38�J>@�Hg�bQ��L����z0-r�I0Gɻ!GI���$�͕�E�"3�X��V9p� (�[��`�=���;.�ډ��}HDžzbN�(KNL %ql� i���z0-�9*L>bR$?7����hקb=�L5]7��?0r�uSH,��")��zVѸ4'X���+�)�*'2Sև����&��BM$�$,� ��ߋ����9���Dv�%i��EJ���x*則�5���gm��DU"��ox��Gƅz��GkمD��q���%�X�n�h �oD�٫���IA��Fo �Bd��A,tQX�)%VW#ꢫ.�ktiqjB���H(�ck=W�y?�9�y�t����s=����?/�����@$^צ=DP�Y���䉇�8�0�!1oD���/I"���<� 5 | ��E���)SVx#m 6 | �*#�Æ4h�=\L��q�#WU��h@F|'E*�Y���$��Z����ua9 3&�,�aN��v�*���Q��J�D9�@���^в���ش�*qXqf6�e�"�D� xF�NS�J� �W�Z�LAݓ�ڄ�,zEX�6^��&vJ�a4# 10 | ��DY���}O���1��F��� "c^ �Y9�Z�pcQ 11 | º�J��K�*b΀M�� ��0vWIgk�-�Y��4��iA�q(~����ӊ�Є3�I�T{ۗr�o]��5(��)c|ق'%�8�UD�2fYP�]?P|��]������Xƴ��ZZ�493$�����1�Ic��Zj��9����32�bb,~Oˁ�q��;c@����wr�K$g (ڍ�����*ڝ}>!�1D#c��uF�vƁhB����NPf� ���� e��F�·�i� L��OwƁ��9SF� !�_gg71������H��@}���o\8�h�rt�U����������5d%mO�wV˦�(��>�|�Y))% �N��L��HV�׆F,&��2�&�m���lH!���RMy���V�������s�O����wN��l��r� ���B,ҡsZ{��;�`s.�]5�f1���'��E1��B�NK��"���v���31]u��LH��8�Sg���VT��&o�$�_Vɍ� �g�Q�b3$NHD:p@:G��ӘD����%������Pl�d��c+�s�(yJ��3D:B<���O����LHD�3dj��c)�r&�k�G�Y����t���D�����x�����3!�H)*O�"v�f#�y����-� Hy �Q��������B�o������1�<�[F�����W5��������y�����-U-�(7����0�WE�B|l�Rl!�T$4Nj4��h�%�@�`� MQ�� �Ll��Y#.C(�U��m���A�����������������y������=���� 9�h��N#��C��hJl�uH��� �t �L����İ� fْvO�����0��ˠ$3���#F�u���� 1�MP�`ɪ�2��h�C �$�ImO[�?Xoo�l�X�Y>]��[8�)�,,� v?$3��k܊�e&���!kZɬ1f���1�<3*E���3�c�]ص37Z7��P=�c�@��K�V��ˮ�h��3f�O�;�A�-�5mDC1��(WA�3���g�|���������<ĘA�|T�����(� 12 | M�Xou�{� ����d.�@��]�yň��3`^�N;�T[f��{ݔ �0f�D��6k/�5X|a�m݆E:f����J'��t�M��H&GCDg�U�0����[�i�P$N3W�(h׭c̀�=�l(��)��^^Ns���D����l�6vѓV�x=ެ����AM�����1��ѓ2E�:[b���.�����C��RӫƄ��4bL�;��I� �� ��i!G��4�I�Q�������3h;e=� ��i���ZQ�_� ʠ�)��s�8��*#W���r���̠��c��6���|1Z@��Ff�P�HzJbQ1Yj渎�ia�E�̀�'��m�� �P��T�>�l�� ��Bi� ��E�)4Ly��YAi�����)�;����_utTc䩡�y"���� թ�Yf@��份�O�Œ��L>���dH����"JJ-��u$��� 7�����A (�����4TH�����,9��s�T4H���q��.,�ͼ�wF�%�����<\G/_Ll����I��"d������K��ؗ���˄O�-�'2�j�E���9W��Yi�&�y{Z���l�U-p�Z�׎��up�!Z��װ�U�p4Z)�������!*�&`� IEND�B`� -------------------------------------------------------------------------------- /test/fixtures/init/expected/grunt.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | module.exports = function(grunt) { 3 | 4 | // Project configuration. 5 | grunt.initConfig({ 6 | // the staging directory used during the process 7 | staging: 'intermediate', 8 | // final build output 9 | output: 'publish', 10 | // filter any files matching one of the below pattern during mkdirs task 11 | // the pattern in the .gitignore file should work too. 12 | exclude: '.git* build/** node_modules/** grunt.js package.json *.md'.split(' '), 13 | mkdirs: { 14 | staging: '' 15 | }, 16 | // concat css/**/*.css files, inline @import, output a single minified css 17 | css: { 18 | 'css/style.css': ['css/**/*.css'] 19 | }, 20 | // Renames JS/CSS to prepend a hash of their contents for easier 21 | // versioning 22 | rev: { 23 | js: 'js/**/*.js', 24 | css: 'css/**/*.css', 25 | img: 'img/**' 26 | }, 27 | // update references in html to revved files 28 | usemin: { 29 | files: ['**/*.html'] 30 | }, 31 | // html minification 32 | html: '', 33 | // Optimizes JPGs and PNGs (with jpegtran & optipng) 34 | img: { 35 | dist: '' 36 | }, 37 | watch: { 38 | files: '', 39 | tasks: 'lint qunit' 40 | }, 41 | 42 | pkg: '', 43 | meta: { 44 | banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + 45 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' + 46 | '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' + 47 | '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + 48 | ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */' 49 | }, 50 | lint: { 51 | files: ['grunt.js', 'js/**/*.js', 'test/**/*.js'] 52 | }, 53 | qunit: { 54 | files: ['test/**/*.html'] 55 | }, 56 | concat: { 57 | dist: { 58 | src: ['js/plugins.js', 'js/main.js'], 59 | dest: 'js/name-0.1.0.js' 60 | } 61 | }, 62 | min: { 63 | dist: { 64 | src: 'js/name-0.1.0.js', 65 | dest: 'js/main.js' 66 | } 67 | }, 68 | jshint: { 69 | options: { 70 | curly: true, 71 | eqeqeq: true, 72 | immed: true, 73 | latedef: true, 74 | newcap: true, 75 | noarg: true, 76 | sub: true, 77 | undef: true, 78 | boss: true, 79 | eqnull: true, 80 | browser: true 81 | }, 82 | globals: { 83 | jQuery: true 84 | } 85 | }, 86 | uglify: {}, 87 | rjs: { 88 | modules: [{ 89 | name: 'main', 90 | }], 91 | dir: 'publish/js', 92 | appDir: 'js', 93 | baseUrl: './', 94 | pragmas: { 95 | doExclude: true 96 | }, 97 | skipModuleInsertion: false, 98 | optimizeAllPluginResources: true, 99 | findNestedDependencies: true 100 | } 101 | }); 102 | 103 | 104 | // in rjs setup, the concat and min task are overriden to use rjs optimizr 105 | grunt.renameTask('concat', '_concat').registerTask('concat', 'rjs (noop)', function() { 106 | grunt.log.writeln('the concat in rjs setup is a noop, rjs optimizer somewhat replace js concatenation'); 107 | }); 108 | grunt.renameTask('min', '_min').registerTask('min', 'rjs'); 109 | 110 | 111 | // uncomment this line if you're using the build script as a grunt plugin 112 | // it should be installed locally, even better if put in your package.json's 113 | // dependency 114 | // 115 | // grunt.loadNpmTasks('node-build-script'); 116 | }; 117 | -------------------------------------------------------------------------------- /test/fixtures/css/expected.css: -------------------------------------------------------------------------------- 1 | article,aside,details,figcaption,figure,footer,header,hgroup,nav,section{display:block}audio,canvas,video{display:inline-block;*display:inline;*zoom:1}audio:not([controls]){display:none}[hidden]{display:none}html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}html,button,input,select,textarea{font-family:sans-serif;color:#222}body{margin:0;font-size:1em;line-height:1.4}::-moz-selection{background:#b3d4fc;text-shadow:none}::selection{background:#b3d4fc;text-shadow:none}a{color:#00e}a:visited{color:#551a8b}a:hover{color:#06e}a:focus{outline:thin dotted}a:hover,a:active{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}blockquote{margin:1em 40px}dfn{font-style:italic}hr{display:block;height:1px;border:0;border-top:1px solid #ccc;margin:1em 0;padding:0}ins{background:#ff9;color:#000;text-decoration:none}mark{background:#ff0;color:#000;font-style:italic;font-weight:700}pre,code,kbd,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}q{quotes:none}q:before,q:after{content:"";content:none}small{font-size:85%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}ul,ol{margin:1em 0;padding:0 0 0 40px}dd{margin:0 0 0 40px}nav ul,nav ol{list-style:none;list-style-image:none;margin:0;padding:0}img{border:0;-ms-interpolation-mode:bicubic;vertical-align:middle}svg:not(:root){overflow:hidden}figure{margin:0}form{margin:0}fieldset{border:0;margin:0;padding:0}label{cursor:pointer}legend{border:0;*margin-left:-7px;padding:0;white-space:normal}button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle}button,input{line-height:normal}button,input[type="button"],input[type="reset"],input[type="submit"]{cursor:pointer;-webkit-appearance:button;*overflow:visible}button[disabled],input[disabled]{cursor:default}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*width:13px;*height:13px}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-decoration,input[type="search"]::-webkit-search-cancel-button{-webkit-appearance:none}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}textarea{overflow:auto;vertical-align:top;resize:vertical}input:invalid,textarea:invalid{background-color:#f0dddd}table{border-collapse:collapse;border-spacing:0}td{vertical-align:top}.chromeframe{margin:.2em 0;background:#ccc;color:#000;padding:.2em 0}.ir{border:0;font:0/0 a;text-shadow:none;color:transparent;background-color:transparent}.hidden{display:none!important;visibility:hidden}.visuallyhidden{border:0;clip:rect(0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.visuallyhidden.focusable:active,.visuallyhidden.focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}.invisible{visibility:hidden}.clearfix:before,.clearfix:after{content:"";display:table}.clearfix:after{clear:both}.clearfix{*zoom:1}@media print{*{ background:transparent!important;color:#000!important;box-shadow:none!important;text-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100%!important}@page{margin:.5cm}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}}@import url(ui/widget.css);body:before{content:' ☺ H E L L O ☺ ';font:10em Helvetica,sans-serif;color:hotPink}.css-reved .img-6{background:url(../img/6.jpg)} -------------------------------------------------------------------------------- /tasks/init/h5bp/readme.md: -------------------------------------------------------------------------------- 1 | **grunt init template for html5-boilerplate** 2 | 3 | --- 4 | 5 | The root folder will be created the first time the init task is run with 6 | `h5bp` template (eg. `grunt init:h5bp`). 7 | 8 | The resulting output depends on the few question grunt asks during the 9 | process. This includes some path rewrites (like renaming the 10 | `css/style.css` file to something else). 11 | 12 | Simply keep pressing enter for the default h5bp layout. 13 | 14 | ## Options 15 | 16 | ## Steps 17 | 18 | ### Project 19 | 20 | ```sh 21 | Please answer the following: 22 | [?] Project name (foo) 23 | [?] Description (The best project ever.) 24 | [?] Version (0.1.0) 25 | [?] Project git repository (git://github.com/USER/REPO.git) 26 | [?] Project homepage (https://github.com/USER/REPO) 27 | [?] Licenses (MIT) 28 | [?] Author name (USER_NAME) 29 | [?] Author email (USER_EMAIL) 30 | [?] Author url (none) 31 | ``` 32 | 33 | ### Layout 34 | 35 | ```sh 36 | Please answer the following: 37 | help: » [D]efault: Standard html5-boilerplate layout 38 | help: 39 | help: » [C]ustom: Specific build of html5-boilerplate. A series of prompt allows 40 | help: explicit file rewrites for common files and directories. 41 | help: 42 | help: » [S]illy: A "silly" build will prompt for every file in the h5bp repository. 43 | help: 44 | [?] Which layout ? ([D]efault, [C]ustom, [S]illy) c 45 | [?] Do you need to make any changes to the above before continuing? 46 | ``` 47 | 48 | ### Files 49 | 50 | Only if `layout=custom` 51 | 52 | ```sh 53 | Please answer the following: 54 | Please answer the following: 55 | help: You can change here the basic layout of the project you're about to 56 | help: create. The defaults values will create a standars h5bp project, but you 57 | help: might want to go for something different like: 58 | help: 59 | help: js/ → javascripts/ 60 | help: css/ → stylesheets/ 61 | help: img/ → images/ 62 | help: 63 | [?] CSS directory (css/) 64 | [?] JavaScript directory (js/) 65 | [?] Image directory (img/) 66 | [?] Do you need to make any changes to the above before continuing? (y/N) 67 | ``` 68 | 69 | Only if `layout=silly` 70 | ```sh 71 | Please answer the following: 72 | Please answer the following: 73 | [?] 404.html (404.html) 74 | [?] apple-touch-icon-114x114-precomposed.png (apple-touch-icon-114x114-precomposed.png) 75 | [?] apple-touch-icon-144x144-precomposed.png (apple-touch-icon-144x144-precomposed.png) 76 | [?] apple-touch-icon-57x57-precomposed.png (apple-touch-icon-57x57-precomposed.png) 77 | [?] apple-touch-icon-72x72-precomposed.png (apple-touch-icon-72x72-precomposed.png) 78 | [?] apple-touch-icon-precomposed.png (apple-touch-icon-precomposed.png) 79 | [?] apple-touch-icon.png (apple-touch-icon.png) 80 | [?] crossdomain.xml (crossdomain.xml) 81 | [?] css/style.css (css/style.css) 82 | [?] favicon.ico (favicon.ico) 83 | [?] grunt.js (grunt.js) 84 | [?] humans.txt (humans.txt) 85 | [?] index.html (index.html) 86 | [?] js/libs/jquery-1.7.2.js (js/libs/jquery-1.7.2.js) 87 | [?] js/libs/jquery-1.7.2.min.js (js/libs/jquery-1.7.2.min.js) 88 | [?] js/libs/modernizr-2.5.3.min.js (js/libs/modernizr-2.5.3.min.js) 89 | [?] js/plugins.js (js/plugins.js) 90 | [?] js/script.js (js/script.js) 91 | [?] readme.md (readme.md) 92 | [?] robots.txt (robots.txt) 93 | [?] Do you need to make any changes to the above before continuing? (y/N) 94 | ``` 95 | 96 | ### gruntfile 97 | 98 | ```sh 99 | Please answer the following: 100 | [?] Is the DOM involved in ANY way? (Y/n) 101 | [?] Will files be concatenated or minified? (Y/n) 102 | [?] Will you have a package.json file? (Y/n) 103 | [?] What is the intermediate/ directory for the build script? (intermediate/) 104 | [?] What it is the final build output directory? (publish/) 105 | [?] What is the CSS directory? (css/) 106 | [?] What is the JS directory? (js/) 107 | [?] What is the IMG directory? (img/) 108 | [?] Do you need to make any changes to the above before continuing? (y/N) 109 | ``` 110 | -------------------------------------------------------------------------------- /tasks/tar.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path'), 3 | fstream = require('fstream'), 4 | zlib = require('zlib'), 5 | tar = require('tar'); 6 | 7 | module.exports = function(grunt) { 8 | 9 | // 10 | // **tar** task is a basic task for tarball creation. Takes two options: the 11 | // directory (`input`) to read from and the tarball file to create 12 | // (`output`). Depending on tarball extension (either .tar or .tgz), tarball 13 | // file is also passed through zlib.Gzip(). 14 | // 15 | // The tar task will try to get the input / output options from different 16 | // sources, in this order: 17 | // 18 | // - cli options for `--input` and `--output` 19 | // - configuration for tar.input and tar.output (usually in project's Gruntfile) 20 | // 21 | // If one of `--input` or `--output` is not defined, then the task looks for 22 | // these values in grunt's config, and fail if either input or output is 23 | // missing. 24 | // 25 | grunt.registerTask('tar', 'Creates a tarball from a directory', function() { 26 | var input = grunt.option('input') || grunt.config('input'), 27 | output = grunt.option('output') || grunt.config('output'), 28 | cb = this.async(); 29 | 30 | if(!input) grunt.log.error('tar: No input value, please specify one.'); 31 | if(!output) grunt.log.error('tar: No output value, please specify one.'); 32 | if(!input || !output) return cb(false); 33 | 34 | // correct extension? 35 | if(!(/tar|tgz/).test(path.extname(output))) { 36 | grunt.log.error('tar: ' + output + ' is not a valid destination. Must ends with .tar or .tgz'); 37 | return cb(false); 38 | } 39 | 40 | grunt.log.write('Creating tarball >> ' + output + ' from ' + input + ' directory...'); 41 | 42 | // 43 | // Note to self: should the tar task use fstream-ignore instead? By default 44 | // and for now, assuming the tar task is used to copy the whole folder, 45 | // without ignore handling. But seems like the correct way to do so, as the 46 | // stuff ignores by .gitignore or other ignore files are likely not 47 | // intended to be copied. 48 | // 49 | var reader = fstream.Reader({ type: 'Directory', path: input }); 50 | grunt.helper('packer', reader, output, function(msg) { return function(e) { 51 | // no errors.. All ok. 52 | if(!e) { 53 | grunt.log.ok(); 54 | return cb(); 55 | } 56 | // hmm, got error, log and fail 57 | grunt.log.error('Oh snap >> ' + msg); 58 | grunt.log.error(e); 59 | return cb(false); 60 | }}); 61 | }); 62 | 63 | // 64 | // **packer** is a simple pass-through stream helper using zlib and 65 | // isaacs/node-tar. Takes a readable fstream (like a fstream.Reader or 66 | // fstream-ignore), pass it through tar.Pack() with default option, then into 67 | // zlib.Gzip() and finally to a writable fs Stream. 68 | // 69 | // gzipping is optional and only happens when dest ends with `.tgz`. Dest 70 | // ending with `.tar` will omit the zlib.Gzip() step. 71 | // 72 | // Error is a curried function.. Not a raw callback, it returns the callback 73 | // to call on completion though, done this way mainly to circumvent the fact that 74 | // grunt's async callback currently handles instanceof Errors as truthy 75 | // value, hence not failing the task. 76 | // 77 | // note to self: now that packer helper is used by more than one task, should 78 | // simplify this error callback stuff (and not rely on curried thing, 79 | // confusing for little value) 80 | // 81 | grunt.registerHelper('packer', function(input, dest, error) { 82 | var stream = input.pipe(tar.Pack()) 83 | .on('error', error('tar creation error' + dest)); 84 | 85 | // if it ends with .tgz, then Gzip it. 86 | if(path.extname(dest) === '.tgz') stream = stream.pipe(zlib.Gzip()); 87 | return stream.on('error', error('gzip error ' + dest)) 88 | .pipe(fstream.Writer({ type: 'File', path: dest })) 89 | .on('error', error('Could not write ' + dest)) 90 | .on('close', error()); 91 | }); 92 | }; 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /lib/plugins/script.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs'), 3 | path = require('path'), 4 | rjs = require('requirejs'), 5 | mkdirp = require('mkdirp'); 6 | 7 | var plugin = module.exports; 8 | 9 | // give it a name 10 | plugin.name = 'script'; 11 | 12 | // give it some defaults 13 | plugin.defaults = { 14 | dir: process.cwd(), 15 | output: 'js/bundle.min.js' 16 | }; 17 | 18 | // 19 | // Second plugin implementation. 20 | // 21 | // Will probably flesh out and remove duplicated code. 22 | // 23 | 24 | // and the main plugin handler, mixed in jsdom's jquery as $.fn.pluginame 25 | plugin.handler = function link($, options, cb) { 26 | options = options || {}; 27 | 28 | // hmm, will probably do this in other way, log object from grunt is passed 29 | // in options, or anything that comply to the grunt logger api. Using 30 | // `writeln` as of now. Will probably work out a better adapter system. 31 | var log = options.log || { writeln: $.noop }; 32 | 33 | // same goes for some usefull helpers, here we need minify 34 | var min = (options.helpers && options.helpers.uglify) || $.noop; 35 | 36 | options.separator = process.platform === 'win32' ? '\r\n' : '\n'; 37 | options.minify = typeof options.minify !== 'undefined' ? options.minify : true; 38 | 39 | // reg cache 40 | var rAbs = /\/\//; 41 | 42 | // don't act on zero-element 43 | if(!this.length) { 44 | cb(); 45 | return this; 46 | } 47 | 48 | // don't handle external files 49 | var set = this.filter(function(i, el) { 50 | var src = $(el).attr('src'); 51 | return !rAbs.test(src); 52 | }); 53 | 54 | // size of the passed in collection, after absolute url filter 55 | // used to emit `success` on last iteration. 56 | var ln = set.length; 57 | log.writeln(' › Script plugin - about to process ' + set.length + ' element(s)'); 58 | 59 | // will hold the concatenated script, 60 | // in the order they appear in the collection 61 | var files = []; 62 | 63 | // 64 | // 1. Iterates through each script in order, append them to the files array 65 | // 2. On the very last iteration (last script tags in jQuery collection), 66 | // concat'd files are minified. 67 | // 3. The single minified content string is written to `options.output` or to 68 | // the `data-build` attribute of last script tag. 69 | // 4. Every script in the collection but the last one get removed. Last 70 | // one is updated so that the src attribute is `src=path/to/output` 71 | // 72 | // todo: better doc, further customization on this default behaviour 73 | // 74 | 75 | return set.each(function(i, target) { 76 | var el = $(this), 77 | last = ln === (i + 1), 78 | src = el.attr('src'), 79 | file = path.resolve(options.cwd, src); 80 | 81 | if(!path.existsSync(file)) return cb(new Error('no ' + src)); 82 | 83 | files = files.concat(fs.readFileSync(file, 'utf8')); 84 | 85 | log.writeln(' › Handle: ' + el.get(0).outerHTML); 86 | if(!last) return el.remove(); 87 | 88 | var href = el.data('build') || options.output, 89 | output = path.join(options.out, href); 90 | 91 | log.writeln((' › Writing optimized JS file to output ' + output).bold); 92 | 93 | mkdirp(path.dirname(output), function(e) { 94 | if(e) return cb(e); 95 | el.attr('src', href); 96 | var content = files.join(options.separator); 97 | 98 | // 99 | // minify - depending on the number of files to process, and their content 100 | // minification is a quite costly process. We should store in some cache 101 | // the minified output, indexed by the sha1 of concatenated files. 102 | // 103 | if(options.minify) log 104 | .writeln((' › Minifying JS file to output ' + output).bold) 105 | .writeln((' › from ' + set.length + ' file(s)').bold) 106 | .writeln((' › This may take a while..').bold); 107 | 108 | content = options.minify ? min(content) : content; 109 | fs.writeFile(output, content, cb); 110 | }); 111 | }); 112 | }; 113 | 114 | -------------------------------------------------------------------------------- /test/fixtures/default/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 46 | 47 | 48 | 49 | 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 | 85 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /test/fixtures/default/expected.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 46 | 47 | 48 | 49 | 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 | 85 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /test/fixtures/usemin/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 50 | 51 | 52 | 53 | 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 | 89 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /test/tasks/test-usemin.js: -------------------------------------------------------------------------------- 1 | // 2 | // Mocha generated tests 3 | // 4 | 5 | var fs = require("fs"), 6 | path = require("path"), 7 | assert = require("assert"), 8 | helpers = require("../helpers"); 9 | 10 | describe("USEMIN task", function() { 11 | 12 | before(helpers.before); 13 | 14 | before(function(done) { 15 | helpers.copy(['usemin/tmpl.hbs', 'usemin/tmpl.mustache'], '.test/views', done); 16 | }); 17 | 18 | describe("As a build script user I want to be able to run the usemin task So that I can see the usemin task in action", function() { 19 | 20 | describe("usemin task", function() { 21 | 22 | it("Given I run the 'usemin' task", function(done) { 23 | // runt the usemin task 24 | helpers.run("usemin", done); 25 | }); 26 | 27 | it("When the script ends", function(done) { 28 | // not doing anything particularly usefull in this step 29 | // but the hook is here if we need to 30 | done(); 31 | }); 32 | 33 | it("Then '.test/usemin.html' should be the same as 'test/fixtures/usemin/index.html'", function(done) { 34 | // todo: task log output doesn"t return things that were changed between 35 | // directives 36 | helpers.assertFile(".test/usemin.html", "test/fixtures/usemin/index.html"); 37 | done(); 38 | }); 39 | 40 | }); 41 | 42 | 43 | describe("usemin task with reved imgs", function() { 44 | 45 | before (function(done) { 46 | // XXX move into an helper function, used here and in test-css.js 47 | // copy in some files, with @imports to test out the inline imports 48 | var files = fs.readdirSync(path.join(__dirname, "../fixtures/css")) 49 | .filter(function(f) { 50 | return !(/expected/).test(f) & !!path.extname(f); 51 | }) 52 | .map(function(f) { 53 | return path.join("css", f); 54 | }); 55 | 56 | helpers.copy(files, ".test/css", function(err) { 57 | if(err) return done(err); 58 | // add one level of relative assets, just to see how it goes 59 | helpers.copyFile('test/fixtures/css/ui/widget.css', '.test/css/ui/widget.css', done); 60 | }); 61 | }); 62 | 63 | it("Given I run the 'rev usemin' task", function(done) { 64 | // runt the usemin task 65 | helpers.run("css rev usemin", done); 66 | }); 67 | 68 | it("When the script ends", function(done) { 69 | // not doing anything particularly usefull in this step 70 | // but the hook is here if we need to 71 | done(); 72 | }); 73 | 74 | it("Then '.test/usemin.html' should be the same as 'test/fixtures/usemin/index.html'", function(done) { 75 | helpers.assertFile(".test/usemin.html", "test/fixtures/usemin/reved.html"); 76 | done(); 77 | }); 78 | 79 | it("And I should see 'img/59928801.1.png' in '.test/usemin.html'", function(done) { 80 | var test = new RegExp("img/59928801.1.png"); 81 | fs.readFile(".test/usemin.html", function(err, body) { 82 | if(err) return done(err); 83 | assert.ok(test.test(body)); 84 | done(); 85 | }); 86 | }); 87 | 88 | // XXX add step definition for this one. Should readdir the ./test/css 89 | // dir, guessing the revision instead of hardcoding here 90 | it("And I should see 'img/f67f4a27.6.jpg' in '.test/css/style.css'", function(done) { 91 | var test = new RegExp("img/f67f4a27.6.jpg"); 92 | fs.readFile(".test/css/e1823e1a.style.css", function(err, body) { 93 | if(err) return done(err); 94 | assert.ok(test.test(body), 'Missing reved img in style.css'); 95 | done(); 96 | }); 97 | }); 98 | 99 | it("And '.test/views/tmpl.hbs' should be the same as 'test/fixtures/usemin/expected/tmpl.hbs'", function(done) { 100 | helpers.assertFile(".test/views/tmpl.hbs", "test/fixtures/usemin/expected/tmpl.hbs"); 101 | done(); 102 | }); 103 | 104 | it("And '.test/views/tmpl.mustache' should be the same as 'test/fixtures/usemin/expected/tmpl.mustache'", function(done) { 105 | helpers.assertFile(".test/views/tmpl.mustache", "test/fixtures/usemin/expected/tmpl.mustache"); 106 | done(); 107 | }); 108 | 109 | }); 110 | 111 | }); 112 | 113 | }); 114 | -------------------------------------------------------------------------------- /tasks/init/h5bp/gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module:false*/ 2 | module.exports = function(grunt) { 3 | 4 | // Project configuration. 5 | grunt.initConfig({ 6 | // the staging directory used during the process 7 | staging: '{%= staging %}', 8 | // final build output 9 | output: '{%= output %}', 10 | 11 | // Create the build dirs 12 | mkdirs: { 13 | staging: './' 14 | }, 15 | 16 | // concat css/**/*.css files, inline @import, output a single minified css 17 | css: { 18 | 'css/main.css': ['{%= css_dir %}/**/*.css'] 19 | }, 20 | 21 | // Renames JS/CSS to prepend a hash of their contents for easier 22 | // versioning 23 | rev: { 24 | js: '{%= js_dir %}/**/*.js', 25 | css: '{%= css_dir %}/**/*.css', 26 | img: '{%= img_dir %}/**' 27 | }, 28 | 29 | // update references in html to revved files 30 | usemin: { 31 | html: ['**/*.html'], 32 | css: ['**/*.css'] 33 | }, 34 | 35 | // html minification 36 | html: { 37 | files: '' 38 | }, 39 | 40 | // Optimizes JPGs and PNGs (with jpegtran & optipng) 41 | img: { 42 | dist: '' 43 | }, 44 | 45 | watch: { 46 | files: '', 47 | tasks: 'lint {%= test_task %}' 48 | }, 49 | 50 | {% if (min_concat || require_js) { if (package_json) { %} 51 | pkg: '', 52 | meta: { 53 | banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + 54 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' + 55 | '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' + 56 | '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + 57 | ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */' 58 | },{% } else { %} 59 | meta: { 60 | version: '0.1.0', 61 | banner: '/*! PROJECT_NAME - v<%= meta.version %> - ' + 62 | '<%= grunt.template.today("yyyy-mm-dd") %>\n' + 63 | '* http://PROJECT_WEBSITE/\n' + 64 | '* Copyright (c) <%= grunt.template.today("yyyy") %> ' + 65 | 'YOUR_NAME; Licensed MIT */' 66 | },{% } } %} 67 | lint: { 68 | files: ['grunt.js', '{%= js_dir %}/**/*.js', '{%= test_dir %}/**/*.js'] 69 | },{% if (dom) { %} 70 | qunit: { 71 | files: ['{%= test_dir %}/**/*.html'] 72 | },{% } else { %} 73 | test: { 74 | files: ['{%= test_dir %}/**/*.js'] 75 | },{% } %}{% if (min_concat) { %} 76 | concat: { 77 | dist: { 78 | src: ['{%= js_dir %}/plugins.js', '{%= js_dir %}/main.js'], 79 | dest: '{%= js_dir %}/{%= name %}-{%= version %}.js' 80 | } 81 | }, 82 | min: { 83 | dist: { 84 | src: '{%= js_dir %}/{%= name %}-{%= version %}.js', 85 | dest: '{%= js_dir %}/main.js' 86 | } 87 | },{% } %} 88 | jshint: { 89 | options: { 90 | curly: true, 91 | eqeqeq: true, 92 | immed: true, 93 | latedef: true, 94 | newcap: true, 95 | noarg: true, 96 | sub: true, 97 | undef: true, 98 | boss: true, 99 | eqnull: true{% if (dom) { %}, 100 | browser: true{% } %} 101 | }, 102 | globals: {{% if (jquery) { %} 103 | jQuery: true 104 | {% } %}} 105 | }{% if (min_concat) { %}, 106 | uglify: {}{% } %}{% if (require_js) { %}, 107 | rjs: { 108 | modules: [{ 109 | name: 'main', 110 | }], 111 | dir: 'js/', 112 | appDir: 'js', 113 | baseUrl: './', 114 | pragmas: { 115 | doExclude: true 116 | }, 117 | skipModuleInsertion: false, 118 | optimizeAllPluginResources: true, 119 | findNestedDependencies: true 120 | }{% } %} 121 | }); 122 | 123 | {% if (require_js) { %} 124 | // in rjs setup, the concat and min task are overriden to use rjs optimizr 125 | grunt.renameTask('concat', '_concat').registerTask('concat', 'rjs (noop)', function() { 126 | grunt.log.writeln('the concat in rjs setup is a noop, rjs optimizer somewhat replace js concatenation'); 127 | }); 128 | grunt.renameTask('min', '_min').registerTask('min', 'rjs'); 129 | {% } %} 130 | 131 | // uncomment this line if you're using the build script as a grunt plugin 132 | // it should be installed locally, even better if put in your package.json's 133 | // dependency 134 | // 135 | // grunt.loadNpmTasks('node-build-script'); 136 | }; 137 | -------------------------------------------------------------------------------- /test/fixtures/usemin/reved.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 50 | 51 | 52 | 53 | 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 | 89 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /test/fixtures/default/usemin.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 35 | 52 | 53 | 54 | 55 | 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 | 94 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /tasks/html.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path'), 3 | minifier = require('html-minifier'); 4 | 5 | // 6 | // This task takes care of html minification through @kangax' html-minifier 7 | // project 8 | // 9 | // > http://perfectionkills.com/experimenting-with-html-minifier/ 10 | // 11 | // This is not a multi-task, but a simple one. The configuration is as follow: 12 | // 13 | // ... 14 | // html: { 15 | // files: ['**/*.html'] 16 | // options: { 17 | // ... 18 | // } 19 | // } 20 | // ... 21 | // 22 | // Usage: 23 | // 24 | // grunt html: 25 | // 26 | // Three "types" of html compression are supported. They map the html 27 | // options of h5bp/ant-build-script, except that types and configuration works 28 | // in "reverse order" here. (eg. default type is most aggresive compression). 29 | // And we at the moment only have two types of html compression. 30 | // 31 | // * compress: (when `grunt html` or `grunt html:compress` is run) 32 | // Agrresive html compression (Most advanced optimization configuration, 33 | // Full html minification) 34 | // 35 | // * buildkit: (when `grunt html:buildkit` is run) 36 | // all html whitespace/comments maintained (todo: inline and minify 37 | // style/script) 38 | // 39 | // * basics: (when `grunt html:basics` is run) Intermediate html compression 40 | // (whitespace removed / comments removed) 41 | // 42 | // One very last thing this tasks needs to do is the minification of 43 | // inlined styles / scripts. 44 | // 45 | // 46 | 47 | var options = { 48 | // maintaining whitespace, removing html comments, extra quotes 49 | // removed, ... 50 | basics: { 51 | collapseWhitespace: false 52 | }, 53 | 54 | // maintaining whitespace, retain html comments 55 | buildkit: { 56 | collapseWhitespace : false, 57 | removeComments : false, 58 | removeCommentsFromCDATA : false, 59 | }, 60 | 61 | compress: { 62 | removeComments : true, 63 | removeCommentsFromCDATA : true, 64 | removeEmptyAttributes : true, 65 | cleanAttributes : true, 66 | removeAttributeQuotes : true, 67 | removeRedundantAttributes : true, 68 | removeScriptTypeAttributes : true, 69 | removeStyleLinkTypeAttributes : true, 70 | collapseWhitespace : true, 71 | collapseBooleanAttributes : true, 72 | removeOptionalTags : true 73 | } 74 | }; 75 | 76 | module.exports = function(grunt) { 77 | 78 | grunt.registerTask('html', 'Basic to aggresive html minification', function(type) { 79 | var config = grunt.config('html') || {}; 80 | grunt.config.requires('html.files', 'staging'); 81 | 82 | grunt.log.writeln('Run htmlcompressor on ' + grunt.log.wordlist(config.files)); 83 | 84 | // default type 85 | type = type || config.type || 'compress'; 86 | 87 | var valid = !!~Object.keys(options).indexOf(type); 88 | if(!valid) return grunt.log.error('not a valid target: ' + type); 89 | 90 | // merge default options for predefined type with the grunt's config 91 | // one. 92 | var defaults = options.compress; 93 | grunt.utils._.defaults(options[type], defaults); 94 | 95 | grunt.log.write('>> ' + type + '...').subhead('Options:'); 96 | grunt.helper('inspect', options[type]); 97 | 98 | var files = grunt.file.expandFiles(config.files).map(function(file) { 99 | var body = grunt.file.read(file); 100 | return { 101 | file: file, 102 | body: body, 103 | minified: grunt.helper('html', body, options[type]) 104 | }; 105 | }); 106 | 107 | // now write back to the disk each optimized html file 108 | files.forEach(function(file) { 109 | grunt.log.subhead(file.file); 110 | grunt.helper('min_max_info', file.minified, file.body); 111 | grunt.file.write(file.file, file.minified); 112 | }); 113 | }); 114 | 115 | // 116 | // **html** helper is a wrapper to html-minifier package, taking care of 117 | // html compression. See below for the full list of possible options. Options 118 | // may be setup using `grunt.config('html.options')` in your gruntfile. 119 | // 120 | grunt.registerHelper('html', function(body, opts) { 121 | opts = opts || {}; 122 | 123 | // > http://perfectionkills.com/experimenting-with-html-minifier/#options 124 | grunt.utils._.defaults(opts, grunt.config('html.options'), { 125 | removeComments: true, 126 | removeCommentsFromCDATA: true, 127 | removeEmptyAttributes: true, 128 | cleanAttributes: true, 129 | removeAttributeQuotes: true, 130 | removeRedundantAttributes: true, 131 | removeScriptTypeAttributes: true, 132 | removeStyleLinkTypeAttributes: true, 133 | collapseWhitespace: true, 134 | collapseBooleanAttributes: true, 135 | removeOptionalTags: true 136 | }); 137 | 138 | return minifier.minify(body, opts); 139 | }); 140 | 141 | }; 142 | 143 | -------------------------------------------------------------------------------- /tasks/serve.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs'), 3 | path = require('path'), 4 | connect = require('connect'), 5 | socketio = require('socket.io'); 6 | 7 | // client-side socket-io script 8 | var ioScript = fs.readFileSync(path.join(__dirname, '../lib/support/reload.js'), 'utf8'); 9 | 10 | module.exports = function(grunt) { 11 | 12 | grunt.registerTask('server', 'Start a custom static web server.', function(target) { 13 | if(!grunt.config('server')) grunt.config('server', { 14 | staging: { port: 3000, base: 'intermediate/' }, 15 | output: { port: 3001, base: 'publish/' } 16 | }); 17 | 18 | if(target) this.requiresConfig('server.' + target); 19 | // long running process 20 | var cb = this.async(); 21 | // Project configuration. 22 | var config = grunt.config('server'); 23 | // if reload flag is setup, in server config or via `--reload` cli option 24 | // then starts a socket.io server instead 25 | var reload = config.reload || grunt.option('reload'); 26 | // the helper name to use 27 | var helper = reload ? 'serve.io' : 'serve'; 28 | 29 | // start the webserver(s) 30 | if(target) return grunt.helper(helper, config[target]); 31 | grunt.helper(helper, config.staging); 32 | grunt.helper(helper, config.output); 33 | }); 34 | 35 | // **serve** creates and returns a basic application server, a configuration 36 | // object may be passed where: 37 | // 38 | // - base - is the base directory to server static file from 39 | // - port - server listen to this port 40 | grunt.registerHelper('server', function(config) { 41 | config = config || {}; 42 | var base = config.base || './', 43 | port = config.port || 8000, 44 | app = connect(); 45 | 46 | app.listen(port, function(err) { 47 | if(err) grunt.log.error(err).warn(err.message, err.code); 48 | grunt.log.ok('Started static web server in ' + base.underline.bold + ' on port ' + (port + '').green + '...'); 49 | app.emit('start'); 50 | }); 51 | 52 | return app; 53 | }); 54 | 55 | // **serve.io** creates and returns a webserver and setup a socket.io instance and 56 | // the "inject" middleware. `/socket.io/socket.io.js` and `/relod.js` are 57 | // then available. 58 | grunt.registerHelper('serve.io', function serve(config) { 59 | // start on given port / base 60 | var app = grunt.helper('server', config); 61 | 62 | app 63 | .use(grunt.helper('inject.io', config)) 64 | .use(connect.static(config.base)); 65 | 66 | // setup scoket.io 67 | var io = socketio.listen(app); 68 | io.set('log level', config.loglevel || 1); 69 | // and return socket.io server instance 70 | return io; 71 | }); 72 | 73 | // **serve** same as serve.io, without the socket.io connection and inject 74 | // middleware. 75 | grunt.registerHelper('serve', function serve(config) { 76 | return grunt.helper('server', config) 77 | .use(connect.static(config.base)); 78 | }); 79 | 80 | // **inject.io** is a grunt helper returning a valid connect / express middleware. 81 | // It's job is to setup a middleware right before the usual static one, and to 82 | // bypass the response of `.html` file to render them with additional scripts. 83 | grunt.registerHelper('inject.io', function(config) { 84 | grunt.utils._.defaults(config, { 85 | hostname: 'localhost' 86 | }); 87 | 88 | return function inject(req, res, next) { 89 | // build filepath from req.url and deal with index files for trailing `/` 90 | var filepath = req.url.slice(-1) === '/' ? req.url + 'index.html' : req.url; 91 | 92 | // the client-side template 93 | var template = grunt.utils._.template(ioScript); 94 | 95 | // deal with our special socket.io client-side script 96 | if(path.basename(filepath) === 'reload.js') { 97 | res.setHeader('Content-Type', connect.static.mime.lookup('js')); 98 | return res.end(template(config)); 99 | } 100 | 101 | // if ext is anything but .html, let it go through usual connect static 102 | // middleware 103 | if(path.extname(filepath) !== '.html') return next(); 104 | 105 | // setup some basic headers, might add some. 106 | res.setHeader('Content-Type', connect.static.mime.lookup(filepath)); 107 | 108 | // can't use the ideal stream / pipe case, we need to alter the html response 109 | // by injecting that little socket.io client-side app. 110 | filepath = path.join(config.base, filepath.replace(/^\//, '')); 111 | fs.readFile(filepath, function(e, body) { 112 | if(e) { 113 | res.writeHead(500); 114 | return res.end('Error loading' + req.url + ' -- ' + JSON.stringify(e)); 115 | } 116 | 117 | body = body.toString().replace(/<\/script>/, function(w) { 118 | return [ 119 | w, 120 | ' ', 121 | ' ' 122 | ].join('\n'); 123 | }); 124 | 125 | res.end(body); 126 | }); 127 | 128 | return next(); 129 | }; 130 | }); 131 | 132 | }; 133 | -------------------------------------------------------------------------------- /tasks/dom.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs'), 3 | path = require('path'); 4 | 5 | // the jQuery file content passed in jsdom 6 | var jquery = fs.readFileSync(path.join(__dirname, '../lib/support/jquery.min.js'), 'utf8'); 7 | 8 | module.exports = dom; 9 | dom.processFile = processFile; 10 | 11 | // attach helpers if any 12 | 13 | // 14 | // ### Dom task 15 | // 16 | // > https://github.com/h5bp/node-build-script/wiki/jsdom-implementation 17 | // 18 | // **note**: should probably not write to original files, but do a full 19 | // copy of current directory. So this kinda works with other tasks like 20 | // clean/mkdirs/copy. 21 | // 22 | // **note**: only supported on posix for now. 23 | // 24 | // **note**: have the feeling that it should be a separate plugin, too much headache 25 | // trying to handle windows + jsdom as the package won't install properly, thus the 26 | // entire node-build-script package (but ok on posix) 27 | // 28 | function dom(grunt) { 29 | 30 | // Grunt utilities. 31 | var task = grunt.task, 32 | file = grunt.file, 33 | log = grunt.log, 34 | config = grunt.config; 35 | 36 | // dom based task only supported on posix for now 37 | if(process.platform === 'win32') return; 38 | 39 | grunt.registerTask('dom', 'Dom-based build system', function() { 40 | var jsdom = dom.jsdom || (dom.jsdom = ensures('jsdom')); 41 | if(!jsdom) return console.log('help install dom'); 42 | 43 | config.requires('dom'); 44 | var conf = config('dom'), 45 | files = file.expand(conf.files), 46 | cb = this.async(); 47 | 48 | log.writeln('About to process following files\n » ' + log.wordlist(files, '\n » ')); 49 | var selectors = Object.keys(conf).filter(function(key) { return key !== 'files'; }); 50 | log.writeln('with the following set of selectors\n » ' + log.wordlist(selectors, '\n » ')); 51 | 52 | var plugins = selectors 53 | .filter(function(key) { 54 | return key !== 'options'; 55 | }) 56 | .map(function(key) { 57 | return { 58 | el: key, 59 | plugin: conf[key] 60 | }; 61 | }); 62 | 63 | (function run(files) { 64 | var f = files.shift(); 65 | if(!f) return cb(); 66 | 67 | task.helper('dom:plugin', f, plugins, function(err, body) { 68 | if(err) return grunt.fail.warn(err); 69 | // Write the new content, and keep the doctype safe (innerHTML returns 70 | // the whole document without doctype). 71 | 72 | // temporary writing to filename.html.test to not alter original file 73 | log.subhead(' › writing to output ' + f); 74 | fs.writeFileSync(f, '' + body); 75 | 76 | log.subhead(' ✔ ' + f); 77 | run(files); 78 | }); 79 | }(files)); 80 | 81 | }); 82 | 83 | // name will probably change 84 | grunt.registerHelper('dom:plugin', function(f, plugins, cb) { 85 | log.subhead('About to process ' + f); 86 | 87 | var ln = plugins.length; 88 | processFile(f, function(err, window) { 89 | if(err) return cb(err); 90 | 91 | var $ = window.$; 92 | 93 | // augment jQuery namespace accordingly with passed in plugins 94 | plugins.forEach(function(it) { 95 | var plugin = it.plugin; 96 | // silent for now 97 | // if(!plugin.name) return cb(new Error('Require plugin.name missing')); 98 | // if(!plugin.handler) return cb(new Error('Require plugin.handler missing')); 99 | // if(typeof plugin.handler !== 'function') return cb(new Error('plugin.handler must be a function')); 100 | if(!plugin.name || !plugin.handler) return; 101 | // should do this under a single facade do reduce namespace collision 102 | $.fn[plugin.name] = plugin.handler; 103 | }); 104 | 105 | plugins.forEach(function(it) { 106 | var el = it.el, 107 | plugin = it.plugin, 108 | name = plugin.name; 109 | 110 | log.writeln(' » Handle ' + el); 111 | if(!$.fn[name]) return next(); 112 | var options = $.extend({}, plugin.defaults, config('dom.options')); 113 | // attach some usefull info / api to the options object 114 | options.log = options.log || log; 115 | // same for grunt helpers 116 | 117 | options.helpers = options.helpers || task._helpers; 118 | $(el)[name]($, options, function(err) { 119 | if(err) return cb(err); 120 | next(); 121 | }); 122 | }); 123 | 124 | function next() { 125 | if(--ln) return; 126 | cb(null, window.document.innerHTML, window); 127 | } 128 | }); 129 | }); 130 | } 131 | 132 | 133 | 134 | 135 | // ## Helpers 136 | // 137 | // The process file function is the asyns.forEach iterator function. It tries 138 | // to read the file content from the file system, and bootstrap a jsdom 139 | // environement for each of these. 140 | // 141 | // The processor might have change the dom tree. The content of 142 | // `window.document.innerHTML` is then used to replace the original file. 143 | // 144 | 145 | function processFile(file, jsdom, cb) { 146 | fs.readFile(file, 'utf8', function(err, body) { 147 | if(err) return cb(err); 148 | jsdom.env({ 149 | html: body, 150 | src: [jquery], 151 | done: cb 152 | }); 153 | }); 154 | } 155 | 156 | 157 | // ensures is a wrapper to `require`. Failsafe require catching loading error if 158 | // not installed, displaying a meaningfull help. 159 | function ensures(name) { 160 | return require(name); 161 | } 162 | 163 | -------------------------------------------------------------------------------- /tasks/img.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs'), 3 | path = require('path'), 4 | which = require('which'), 5 | exists = fs.exists || path.exists; 6 | 7 | 8 | // 9 | // This task takes care of img optimizations by running a set of `.png` 10 | // or `.jpg` files through optipng and jpegtran. 11 | // 12 | // grunt img: 13 | // 14 | // Gruntfile config: 15 | // 16 | // ... 17 | // img: { 18 | // src: ['img/**/*'], 19 | // options: { 20 | // ... 21 | // } 22 | // } 23 | // 24 | 25 | var win32 = process.platform === 'win32'; 26 | 27 | module.exports = function(grunt) { 28 | 29 | var png = ['.png', '.bmp', '.gif', '.pnm', '.tiff'], 30 | jpegs = ['.jpg', 'jpeg']; 31 | 32 | // rev task - reving is done in the `output/` directory 33 | grunt.registerMultiTask('img', 'Optimizes .png/.jpg images using optipng/jpegtran', function() { 34 | var cb = this.async(), 35 | files = grunt.file.expandFiles(this.file.src); 36 | 37 | var pngfiles = files.filter(function(file) { 38 | return !!~png.indexOf(path.extname(file).toLowerCase()); 39 | }); 40 | 41 | var jpgfiles = files.filter(function(file) { 42 | return !!~jpegs.indexOf(path.extname(file).toLowerCase()); 43 | }); 44 | 45 | var remains = 2; 46 | grunt.helper('optipng', pngfiles, grunt.config('optipng'), function(err) { 47 | if(err) { 48 | grunt.log.error(err); 49 | return cb(false); 50 | } 51 | 52 | grunt.helper('jpegtran', jpgfiles, grunt.config('jpegtran'), function(err) { 53 | if(err) { 54 | grunt.log.error(err); 55 | return cb(false); 56 | } 57 | cb(); 58 | }); 59 | }); 60 | }); 61 | 62 | grunt.registerHelper('optipng', function(files, opts, cb) { 63 | opts = opts || {}; 64 | cb = cb || function() {}; 65 | 66 | grunt.helper('which', 'optipng', function(err, cmdpath) { 67 | if(err) return grunt.helper('not installed', 'optipng', cb); 68 | var args = opts.args ? opts.args : []; 69 | args = args.concat(files); 70 | if(!files.length) return cb(); 71 | grunt.log.writeln('Running optipng... ' + grunt.log.wordlist(files)); 72 | var optipng = grunt.utils.spawn({ 73 | cmd: cmdpath, 74 | args: args 75 | }, function() {}); 76 | 77 | optipng.stdout.pipe(process.stdout); 78 | optipng.stderr.pipe(process.stderr); 79 | optipng.on('exit', function(code) { 80 | if(code) grunt.warn('optipng exited unexpectedly with exit code ' + code + '.', code); 81 | cb(); 82 | }); 83 | }); 84 | }); 85 | 86 | grunt.registerHelper('jpegtran', function(files, opts, cb) { 87 | opts = opts || {}; 88 | cb = cb || function() {}; 89 | opts.args = opts.args ? opts.args : ['-copy', 'none', '-optimize', '-outfile', 'jpgtmp.jpg']; 90 | 91 | var tmpfile = 'jpgtmp.jpg'; 92 | 93 | grunt.helper('which', 'jpegtran', function(err, cmdpath) { 94 | if(err) return grunt.helper('not installed', 'jpegtran', cb); 95 | 96 | // Remove temp JPEGtran file when finished 97 | function clean() { 98 | exists(tmpfile, function(exists) { 99 | if (!exists) return cb(); 100 | grunt.log.subhead('** Removing: ' + tmpfile); 101 | fs.unlink(tmpfile, function(err) { 102 | if (err) { 103 | grunt.log.error(err); 104 | return cb(false); 105 | } 106 | return cb(); 107 | }); 108 | }); 109 | } 110 | 111 | (function run(file) { 112 | if(!file) return clean(); 113 | grunt.log.subhead('** Processing: ' + file); 114 | var jpegtran = grunt.utils.spawn({ 115 | cmd: cmdpath, 116 | args: opts.args.concat(file) 117 | }, function() {}); 118 | 119 | jpegtran.stdout.pipe(process.stdout); 120 | jpegtran.stderr.pipe(process.stderr); 121 | 122 | jpegtran.on('exit', function(code) { 123 | if(code) return grunt.warn('jpgtran exited unexpectedly with exit code ' + code + '.', code); 124 | // output some size info about the file 125 | grunt.helper('min_max_stat', tmpfile, file); 126 | // copy the temporary optimized jpg to original file 127 | fs.createReadStream(tmpfile) 128 | .pipe(fs.createWriteStream(file)).on('close', function() { 129 | run(files.shift()); 130 | }); 131 | }); 132 | }(files.shift())); 133 | }); 134 | 135 | }); 136 | 137 | grunt.registerHelper('not installed', function(cmd, cb) { 138 | grunt.verbose.or.writeln(); 139 | grunt.log.write('Running ' + cmd + '...').error(); 140 | grunt.log.errorlns([ 141 | 'In order for this task to work properly, :cmd must be', 142 | 'installed and in the system PATH (if you can run ":cmd" at', 143 | 'the command line, this task should work)' 144 | ].join(' ').replace(/:cmd/g, cmd)); 145 | grunt.log.subhead('Skiping ' + cmd + ' task'); 146 | if(cb) cb(); 147 | }); 148 | 149 | // **which** helper, wrapper to isaacs/which package plus some fallback logic 150 | // specifically for the win32 binaries in vendor/ (optipng.exe, jpegtran.exe) 151 | grunt.registerHelper('which', function(cmd, cb) { 152 | if(!win32 || !/optipng|jpegtran/.test(cmd)) return which(cmd, cb); 153 | 154 | var cmdpath = cmd === 'optipng' ? '../vendor/optipng-0.7.1-win32/optipng.exe' : 155 | '../vendor/jpegtran-8d/jpegtran.exe'; 156 | 157 | cb(null, path.join(__dirname, cmdpath)); 158 | }); 159 | }; 160 | 161 | -------------------------------------------------------------------------------- /docs/css/white.css: -------------------------------------------------------------------------------- 1 | /* borrowed to http://backbonejs.org */ 2 | 3 | body, p { 4 | font-family: 'Open Sans', sans-serif; 5 | font-size: 13px; 6 | color: #222; 7 | background-color: #FCFBFD; 8 | } 9 | 10 | a { font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; } 11 | 12 | p small{ 13 | font-size:12px; 14 | color:#999999; 15 | } 16 | 17 | pre { 18 | overflow-x: auto; 19 | } 20 | 21 | #sidebar { 22 | background-color: #eee; 23 | position: fixed; 24 | top: 0; left: 0; bottom: 0; 25 | width: 200px; 26 | overflow-y: auto; 27 | overflow-x: hidden; 28 | -webkit-overflow-scrolling: touch; 29 | padding: 15px 0 30px 30px; 30 | border-right: 1px solid rgba(0, 0, 0, 0.05); 31 | } 32 | 33 | #sidebar::-webkit-scrollbar, 34 | pre::-webkit-scrollbar { 35 | width: 8px; 36 | } 37 | 38 | #sidebar::-webkit-scrollbar-track, 39 | pre::-webkit-scrollbar-track { 40 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); 41 | border-radius: 2px; 42 | } 43 | 44 | #sidebar::-webkit-scrollbar-thumb 45 | pre::-webkit-scrollbar-thumb { 46 | border-radius: 2px; 47 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5); 48 | } 49 | 50 | a.toc_title, a.toc_title:visited { 51 | display: block; 52 | font-weight: bold; 53 | margin-top: 15px; 54 | } 55 | 56 | a.toc_title:hover { 57 | text-decoration: underline; 58 | } 59 | 60 | a, a:visited { color: #222; text-decoration: underline; } 61 | a:hover { color: #888; } 62 | #documentation h1 a { color: #555; text-decoration: none; } 63 | #documentation h1 a:hover { color: #888; } 64 | 65 | ul.toc_section, ul.toc_section ul { 66 | font-size: 11px; 67 | line-height: 14px; 68 | margin: 5px 0 0 0; 69 | padding-left: 0px; 70 | list-style-type: none; 71 | font-family: Lucida Grande; 72 | } 73 | ul.toc_section ul { 74 | padding-left: 1em; 75 | } 76 | .toc_section li { 77 | margin: 0 0 3px 0; 78 | } 79 | .toc_section li:before { content: "» "; } 80 | .toc_section li a { 81 | text-decoration: none; 82 | } 83 | .toc_section li a:hover { 84 | text-decoration: underline; 85 | } 86 | .container { 87 | position: relative; 88 | max-width: 690px; 89 | margin: 30px 0 50px 260px; 90 | } 91 | p { 92 | margin: 20px 0; 93 | } 94 | h1, h2, h3, h4, h5, h6 { 95 | margin: 0; 96 | padding-top: 1.5em; 97 | line-height: 1.1; 98 | } 99 | h2 { 100 | font-size: 20px; 101 | padding-bottom: .5em; 102 | } 103 | #sidebar hr { 104 | margin: 25px 0 0 0; 105 | } 106 | hr { 107 | height: 1px; 108 | border: 0; 109 | padding: 0; 110 | margin: 35px 0 0 0; 111 | background: rgba(0,0,0,0.20); 112 | background: -moz-linear-gradient(left center, rgba(85, 85, 85, 0.88) rgba(85, 85, 85, 0.88) 50%, rgba(85, 85, 85, 0.88)); 113 | background: -webkit-gradient(linear, left top, right top, from(rgba(85, 85, 85, 0)), color-stop(0.5, rgba(0, 0, 0, 0.33)), to(rgba(85, 85, 85, 0))); 114 | } 115 | p > code, li > code { 116 | border: 1px solid #eee; 117 | } 118 | 119 | 120 | @media only screen and (max-width: 480px) { 121 | body { margin: 0; padding: 0 } 122 | p { margin: .5em 0 } 123 | h1, h2, h3, h4, h5, h6 { padding-top: .5em } 124 | #sidebar { 125 | display: none; 126 | } 127 | a.toc_title { margin-top: .2em } 128 | ul.toc_section { font-size: 12px; line-height: 16px } 129 | .container { 130 | margin: 12px; 131 | max-width: none; 132 | } 133 | pre { padding: 2px 8px } 134 | } 135 | 136 | 137 | /* 138 | 139 | Dark style from softwaremaniacs.org (c) Ivan Sagalaev 140 | 141 | */ 142 | 143 | pre, pre code { 144 | display: block; padding: 0.5em; 145 | background: #444; 146 | } 147 | 148 | pre .keyword, 149 | pre .literal, 150 | pre .change, 151 | pre .winutils, 152 | pre .flow, 153 | pre .lisp .title, 154 | pre .tex .special { 155 | color: white; 156 | } 157 | 158 | pre code, 159 | pre .ruby .subst { 160 | color: #DDD; 161 | } 162 | 163 | pre .string, 164 | pre .function .title, 165 | pre .class .title, 166 | pre .haskell .label, 167 | pre .ini .title, 168 | pre .tag .value, 169 | pre .css .rules .value, 170 | pre .preprocessor, 171 | pre .ruby .symbol, 172 | pre .ruby .symbol .string, 173 | pre .ruby .symbol .keyword, 174 | pre .ruby .symbol .keymethods, 175 | pre .ruby .instancevar, 176 | pre .ruby .class .parent, 177 | pre .built_in, 178 | pre .sql .aggregate, 179 | pre .django .template_tag, 180 | pre .django .variable, 181 | pre .smalltalk .class, 182 | pre .javadoc, 183 | pre .ruby .string, 184 | pre .django .filter .argument, 185 | pre .smalltalk .localvars, 186 | pre .smalltalk .array, 187 | pre .attr_selector, 188 | pre .pseudo, 189 | pre .addition, 190 | pre .stream, 191 | pre .envvar, 192 | pre .apache .tag, 193 | pre .apache .cbracket, 194 | pre .tex .command, 195 | pre .input_number { 196 | color: #D88; 197 | } 198 | 199 | pre .comment, 200 | pre .java .annotation, 201 | pre .python .decorator, 202 | pre .template_comment, 203 | pre .pi, 204 | pre .doctype, 205 | pre .deletion, 206 | pre .shebang, 207 | pre .apache .sqbracket, 208 | pre .tex .formula { 209 | color: #777; 210 | } 211 | 212 | pre .keyword, 213 | pre .literal, 214 | pre .css .id, 215 | pre .phpdoc, 216 | pre .function .title, 217 | pre .class .title, 218 | pre .haskell .label, 219 | pre .vbscript .built_in, 220 | pre .sql .aggregate, 221 | pre .rsl .built_in, 222 | pre .smalltalk .class, 223 | pre .xml .tag .title, 224 | pre .diff .header, 225 | pre .chunk, 226 | pre .winutils, 227 | pre .bash .variable, 228 | pre .lisp .title, 229 | pre .apache .tag, 230 | pre .tex .special { 231 | font-weight: bold; 232 | } 233 | 234 | pre .xml .css, 235 | pre .xml .javascript, 236 | pre .xml .vbscript, 237 | pre .tex .formula { 238 | opacity: 0.5; 239 | } 240 | -------------------------------------------------------------------------------- /support/grunt-test.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs'), 3 | path = require('path'), 4 | spawn = require('child_process').spawn, 5 | Parser = require('mocha-gherkin'); 6 | 7 | // 8 | // 9 | // This file defines a few development tasks (not part of the plugin 10 | // functionnality). 11 | // 12 | // - test: basic override of built-in test task to spawn mocha instead. 13 | // 14 | // - genmocha: Run this task to quickly generate new test for a given task. 15 | // 16 | // 17 | // A series of prompt will ask for values which are used to process 18 | // test/features/template.feature. Feature file is then generated at 19 | // test/features/.feature and Mocha test file at 20 | // test/tasks/.js 21 | // 22 | // Add a `--missing` or `--step `option to generate step definition file for 23 | // this feature instead of Mocha test. File is written at 24 | // test/features/steps/.js 25 | // 26 | // The parser will search for a test/features/steps/.js file and use 27 | // it to init mocha assertion snippets. 28 | // 29 | 30 | module.exports = function(grunt) { 31 | 32 | grunt.registerMultiTask('test', 'Redefine the test task to spawn mocha instead', function() { 33 | 34 | // pass in any positional arguments after the `test` task 35 | var args = process.argv.slice(3); 36 | 37 | // expand all the files for this subtask 38 | var files = grunt.file.expandFiles(this.file.src); 39 | 40 | // path to mocha executable, set as devDependencies in package.json 41 | var mocha = path.join(__dirname, '../node_modules/mocha/bin/mocha'); 42 | 43 | // async task 44 | var cb = this.async(); 45 | 46 | // run each file serially, taking care of exiting on first fail 47 | (function run(file) { 48 | if(!file) return cb(); 49 | var child = spawn('node', [mocha].concat(file).concat(args)); 50 | child.stdout.pipe(process.stdout); 51 | child.stderr.pipe(process.stderr); 52 | child.on('exit', function(code) { 53 | if(code) { 54 | grunt.warn(new Error('cmd failed'), code); 55 | return; 56 | } 57 | run(files.shift()); 58 | }); 59 | })(files.shift()); 60 | 61 | }); 62 | 63 | grunt.registerTask('genmocha', 'Output instruction on how to generate a new test', function(taskname) { 64 | var cb = this.async(); 65 | 66 | taskname = taskname || grunt.option('taskname') || 'foo'; 67 | 68 | var missing = grunt.option('missing') || grunt.option('step'); 69 | 70 | // path to the tasks directory 71 | var taskdir = path.join(__dirname, '../tasks'); 72 | 73 | grunt.helper('prompt', {}, [{ 74 | name: 'taskname', 75 | message: 'Which task would you like to test?', 76 | default: taskname, 77 | warning: 'grunt prompts are pretty cool' 78 | }, { 79 | name: 'feature', 80 | message: 'Feature?', 81 | default: taskname.toUpperCase() + ' task' 82 | }, { 83 | name: 'role', 84 | message: 'As a ?', 85 | default: 'build script user' 86 | }, { 87 | name: 'action', 88 | message: 'I want to ?', 89 | default: 'be able to run the ' + taskname + ' task' 90 | }, { 91 | name: 'benefit', 92 | message: 'So that ?', 93 | default: 'I can see the ' + taskname + ' task in action' 94 | }, { 95 | name: 'scenario', 96 | message: 'Scenario: ?', 97 | default: taskname + ' task' 98 | }, { 99 | name: 'context', 100 | message: 'Given ?', 101 | default: 'I run the "'+ taskname + '" task' 102 | }, { 103 | name: 'event', 104 | message: 'When ?', 105 | default: 'the script ends' 106 | }, { 107 | name: 'outcome', 108 | message: 'Then ?', 109 | default: '".test/:task" should be the same as "test/fixtures/:task/expected/"' 110 | .replace(/:task/g, taskname) 111 | }], function(err, props) { 112 | if(err) return grunt.warn(err); 113 | if(!path.extname(props.taskname)) props.taskname = props.taskname + '.js'; 114 | 115 | var exists = path.existsSync(path.join(taskdir, props.taskname)); 116 | if(!exists) { 117 | grunt.log.error('Not a known task + ' + props.taskname); 118 | grunt.log.error(grunt.log.wordlist(fs.readdirSync(taskdir))); 119 | return grunt.warn('failed generating test for ' + props.taskname); 120 | } 121 | 122 | // 1. create the feature 123 | var feature = grunt.file.read('test/features/template.feature'); 124 | feature = grunt.template.process(feature, props); 125 | 126 | // 2. Write feature to test/features 127 | var featurefile = props.taskname.replace(path.extname(props.taskname), '.feature'); 128 | 129 | var filename = 'test/features/' + featurefile; 130 | grunt.log.ok('Generate feature ' + filename); 131 | grunt.file.write(filename, feature); 132 | 133 | // 3. Generate according mocha specs from feature file 134 | 135 | var step = path.join(__dirname, '../test/features/steps', props.taskname); 136 | step = path.existsSync(step) ? step : 'test/features/steps/default.js'; 137 | var parser = new Parser({ 138 | missing: missing, 139 | step: fs.readFileSync(step, 'utf8'), 140 | modules: [{ 141 | name: 'helpers', 142 | path: '../helpers' 143 | }] 144 | }); 145 | 146 | // if --missing flag is turned on, generate step definition instead 147 | var subdir = (missing ? 'features/steps' : 'tasks'); 148 | 149 | var testfile = path.join(__dirname, '../test', subdir, 'test-' + props.taskname); 150 | grunt.log.ok('Generate ' + (missing ? 'step definition file ' : 'testfile ') + testfile); 151 | 152 | parser.pipe(process.stdout); 153 | parser.pipe(fs.createWriteStream(testfile)); 154 | fs.createReadStream(filename).pipe(parser).on('close', cb); 155 | }); 156 | }); 157 | 158 | }; 159 | 160 | 161 | -------------------------------------------------------------------------------- /docs/css/grey.css: -------------------------------------------------------------------------------- 1 | /* borrowed to http://backbonejs.org */ 2 | 3 | body { 4 | font-size: 13px; 5 | line-height: 18px; 6 | color: #555; 7 | background-color: #EDEDED; 8 | font-family: Helvetica Neue, Helvetica, Arial, sans-serif; 9 | } 10 | 11 | pre { 12 | overflow-x: auto; 13 | } 14 | 15 | #sidebar { 16 | background-color: #F3F3F3; 17 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3); 18 | -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3); 19 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3); 20 | 21 | position: fixed; 22 | top: 0; left: 0; bottom: 0; 23 | width: 200px; 24 | overflow-y: auto; 25 | overflow-x: hidden; 26 | -webkit-overflow-scrolling: touch; 27 | padding: 15px 0 30px 30px; 28 | border-right: 1px solid rgba(0, 0, 0, 0.05); 29 | } 30 | 31 | #sidebar::-webkit-scrollbar, 32 | pre::-webkit-scrollbar { 33 | width: 8px; 34 | } 35 | 36 | #sidebar::-webkit-scrollbar-track, 37 | pre::-webkit-scrollbar-track { 38 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); 39 | border-radius: 2px; 40 | } 41 | 42 | #sidebar::-webkit-scrollbar-thumb 43 | pre::-webkit-scrollbar-thumb { 44 | border-radius: 2px; 45 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5); 46 | } 47 | 48 | a.toc_title, a.toc_title:visited { 49 | display: block; 50 | font-weight: bold; 51 | margin-top: 15px; 52 | } 53 | 54 | a.toc_title:hover { 55 | text-decoration: underline; 56 | } 57 | 58 | a, a:visited { color:#366ddc; text-decoration:none; } 59 | a:hover { color:#1d4ba8; text-decoration:underline; } 60 | #documentation h1 a { color: inherit; } 61 | 62 | #sidebar a, #sidebar a:visited { color: #555; } 63 | 64 | ul.toc_section, ul.toc_section ul { 65 | font-size: 11px; 66 | line-height: 14px; 67 | margin: 5px 0 0 0; 68 | padding-left: 0px; 69 | list-style-type: none; 70 | font-family: Lucida Grande; 71 | } 72 | ul.toc_section ul { 73 | padding-left: 1em; 74 | } 75 | .toc_section li { 76 | margin: 0 0 3px 0; 77 | } 78 | .toc_section li:before { content: "» "; } 79 | .toc_section li a { 80 | text-decoration: none; 81 | } 82 | .toc_section li a:hover { 83 | text-decoration: underline; 84 | } 85 | .container { 86 | position: relative; 87 | max-width: 690px; 88 | margin: 30px 0 50px 260px; 89 | } 90 | p { 91 | margin: 20px 0; 92 | } 93 | h1, h2, h3, h4, h5, h6 { 94 | margin: 0; 95 | padding-top: 1.5em; 96 | line-height: 1.1; 97 | } 98 | h2 { 99 | font-size: 20px; 100 | padding-bottom: .5em; 101 | } 102 | #sidebar hr { 103 | margin: 25px 0 0 0; 104 | } 105 | hr { 106 | height: 1px; 107 | border: 0; 108 | padding: 0; 109 | margin: 35px 0 0 0; 110 | background: rgba(0,0,0,0.20); 111 | background: rgba(0,0,0,0.20); 112 | background: -moz-linear-gradient(left center, rgba(85, 85, 85, 0.88) rgba(85, 85, 85, 0.88) 50%, rgba(85, 85, 85, 0.88)); 113 | background: -webkit-gradient(linear, left top, right top, from(rgba(85, 85, 85, 0)), color-stop(0.5, rgba(0, 0, 0, 0.33)), to(rgba(85, 85, 85, 0))); 114 | } 115 | p > code, li > code { 116 | border: 1px solid #eee; 117 | } 118 | 119 | 120 | @media only screen and (device-width: 768px) { 121 | #sidebar { 122 | z-index: 10; 123 | } 124 | #sidebar .toc_section li { 125 | font-size: 16px; 126 | line-height: 22px; 127 | } 128 | pre { white-space: pre-wrap } 129 | } 130 | 131 | @media only screen and (max-width: 480px) { 132 | body { margin: 0; padding: 0 } 133 | p { margin: .5em 0 } 134 | h1, h2, h3, h4, h5, h6 { padding-top: .5em } 135 | #sidebar { 136 | display: none; 137 | box-sizing: border-box; 138 | padding: 12px 16px 30px; 139 | width: 140px; 140 | height: 100%; 141 | font-family: helvetica, sans-serif; 142 | -webkit-transform: translateX(-140px); 143 | -webkit-transition: -webkit-transform 0.3s ease-out; 144 | } 145 | .navigating #sidebar { 146 | display: block; 147 | -webkit-transform: translateX(0); 148 | } 149 | a.toc_title { margin-top: .2em } 150 | ul.toc_section { font-size: 12px; line-height: 16px } 151 | .container { 152 | margin: 12px; 153 | max-width: none; 154 | } 155 | pre { padding: 2px 8px } 156 | } 157 | 158 | /* 159 | 160 | Zenburn style from voldmar.ru (c) Vladimir Epifanov 161 | based on dark.css by Ivan Sagalaev 162 | 163 | */ 164 | 165 | pre, pre code { 166 | display: block; padding: 0.5em; 167 | background: #3F3F3F; 168 | color: #DCDCDC; 169 | } 170 | 171 | pre .keyword, 172 | pre .tag, 173 | pre .django .tag, 174 | pre .django .keyword, 175 | pre .css .class, 176 | pre .css .id, 177 | pre .lisp .title { 178 | color: #E3CEAB; 179 | } 180 | 181 | pre .django .template_tag, 182 | pre .django .variable, 183 | pre .django .filter .argument { 184 | color: #DCDCDC; 185 | } 186 | 187 | pre .number, 188 | pre .date { 189 | color: #8CD0D3; 190 | } 191 | 192 | pre .dos .envvar, 193 | pre .dos .stream, 194 | pre .variable, 195 | pre .apache .sqbracket { 196 | color: #EFDCBC; 197 | } 198 | 199 | pre .dos .flow, 200 | pre .diff .change, 201 | pre .python .exception, 202 | pre .python .built_in, 203 | pre .literal, 204 | pre .tex .special { 205 | color: #EFEFAF; 206 | } 207 | 208 | pre .diff .chunk, 209 | pre .ruby .subst { 210 | color: #8F8F8F; 211 | } 212 | 213 | pre .dos .keyword, 214 | pre .python .decorator, 215 | pre .class .title, 216 | pre .haskell .label, 217 | pre .function .title, 218 | pre .ini .title, 219 | pre .diff .header, 220 | pre .ruby .class .parent, 221 | pre .apache .tag, 222 | pre .nginx .built_in, 223 | pre .tex .command, 224 | pre .input_number { 225 | color: #efef8f; 226 | } 227 | 228 | pre .dos .winutils, 229 | pre .ruby .symbol, 230 | pre .ruby .symbol .string, 231 | pre .ruby .symbol .keyword, 232 | pre .ruby .symbol .keymethods, 233 | pre .ruby .string, 234 | pre .ruby .instancevar { 235 | color: #DCA3A3; 236 | } 237 | 238 | pre .diff .deletion, 239 | pre .string, 240 | pre .tag .value, 241 | pre .preprocessor, 242 | pre .built_in, 243 | pre .sql .aggregate, 244 | pre .javadoc, 245 | pre .smalltalk .class, 246 | pre .smalltalk .localvars, 247 | pre .smalltalk .array, 248 | pre .css .rules .value, 249 | pre .attr_selector, 250 | pre .pseudo, 251 | pre .apache .cbracket, 252 | pre .tex .formula { 253 | color: #CC9393; 254 | } 255 | 256 | pre .shebang, 257 | pre .diff .addition, 258 | pre .comment, 259 | pre .java .annotation, 260 | pre .template_comment, 261 | pre .pi, 262 | pre .doctype { 263 | color: #7F9F7F; 264 | } 265 | 266 | pre .xml .css, 267 | pre .xml .javascript, 268 | pre .xml .vbscript, 269 | pre .tex .formula { 270 | opacity: 0.5; 271 | } 272 | 273 | --------------------------------------------------------------------------------