Permalink
Browse files

Added docs and updated plugin

  • Loading branch information...
1 parent 6744b8d commit cc5d8fdf015e3099300c11be5ab805fbcc813cc1 @sergeche committed Nov 23, 2012
Showing with 133 additions and 33 deletions.
  1. +3 −0 .gitignore
  2. +59 −0 README.md
  3. +24 −23 tasks/frontend.js
  4. +47 −10 test/frontend.js
View
3 .gitignore
@@ -1,2 +1,5 @@
/node_modules/
tmp
+
+/test/out/
+/.build-catalog.json
View
59 README.md
@@ -0,0 +1,59 @@
+A [Grunt.js](http://gruntjs.com) task that compiles CSS and JS files with respect of _file modification_ date. For JS, it uses built-in [UglifyJS](https://github.com/mishoo/UglifyJS) minifier, for CSS — Yandex’s [CSSO](https://github.com/css/csso) with automatic `@import`ed files inclusion.
+
+Unlike basic minifiers, this task generates a hidden catalog file (`.build-catalog.json`) that stores state, last compilation date and md5 hashes of minified files. Every time you call `frontend` task, it will look into this catalog and check if the state of files being minified was changed. If not, the file _will not_ be re-minified which saves CPU time and _modification date_. This date (or md5 hash) can be used to modify URLs to minified files for effective caching.
+
+## Usage ##
+
+This plugin provides `frontend` task. Here’s example `grunt.js` file:
+
+```js
+module.exports = function(grunt) {
+
+ // Project configuration.
+ grunt.initConfig({
+ // Configure 'frontend' task
+ frontendConfig: {
+ // Force file minification even if they were not modified
+ force: false,
+
+ // Path to project sources root folder.
+ // It is used to resolve absolute paths in CSS imports,
+ // for example: @import "/css/file.css" will be resolved
+ // to './src/files/css/file.css'
+ srcWebroot: './src/files',
+
+ // Destination root folder.
+ // Used to update minified files paths in catalog,
+ // e.g. instead of storing '/Users/foo/project/out/css/minified.css' path,
+ // task will cut-out path to webroot store '/css/minified.css' instead
+ webroot: './out'
+ },
+
+ frontend: {
+ // CSS subtask: find all CSS files in `src` folder and
+ // store minified version in `dest` folder
+ css: {
+ src: './src/css',
+ dest: './out/css'
+ },
+
+ // JS subtask: works pretty much the same as default
+ // `min` task:
+ // https://github.com/gruntjs/grunt/blob/master/docs/task_min.md
+ js: {
+ './out/js/f1.js': [
+ './src/js/fileA.js',
+ './src/js/fileB.js'
+ ],
+
+ './out/js/f2.js': [
+ './src/js/fileC.js',
+ './src/js/fileD.js'
+ ]
+ }
+ }
+ });
+};
+```
+
+This task can be used together with [docpad-plugin-frontend](https://github.com/sergeche/docpad-plugin-frontend) to automatically generate cache-effective URLs to assets for [DocPad](https://github.com/bevry/docpad)-generated web-site.
View
47 tasks/frontend.js
@@ -1,6 +1,6 @@
"use strict";
-exports = function(grunt) {
+module.exports = function(grunt) {
var cssModule = require('../lib/css');
var crypto = require('crypto');
var path = require('path');
@@ -128,10 +128,13 @@ exports = function(grunt) {
return shouldCompile;
}
- function compileCSSTask(ctx) {
- grunt.log.writeln('Compiling CSS');
- var data = ctx.data;
- var config = getConfig(ctx);
+ /**
+ * Register reusable CSS compiler helper
+ * @param {Object} data Task payload
+ * @param {Object} config Task config (can be obtained from getConfig() method)
+ * @param {Object} catalog Pointer resources to catalog (can be obtained fron loadCatalog())
+ */
+ grunt.registerHelper('frontend-css', function(data, config, catalog) {
if (!('src' in data)) {
return grunt.fail('No "src" property specified for "css" task');
}
@@ -140,7 +143,7 @@ exports = function(grunt) {
return grunt.fail('No "dest" property specified for "css" task');
}
- var catalog = loadCatalog();
+ catalog = catalog || {};
var force = config.force;
var srcDir = makeAbsPath(data.src);
@@ -183,16 +186,13 @@ exports = function(grunt) {
}
});
- // update catalog
- saveCatalog(catalog);
- }
+ return catalog;
+ });
- function compileJSTask(ctx) {
- grunt.log.writeln('Compiling JS');
- var config = getConfig(ctx);
- var catalog = loadCatalog();
+ grunt.registerHelper('frontend-js', function(data, config, catalog) {
+ catalog = catalog || {};
- var failed = _.find(ctx.data, function(src, dest) {
+ var failed = _.find(data, function(src, dest) {
// a copy on "min" task
var files = grunt.file.expandFiles(src);
var absDestPath = makeAbsPath(dest, config.webroot);
@@ -223,7 +223,7 @@ exports = function(grunt) {
grunt.file.write(dest, min);
// Fail task if errors were logged.
- if (ctx.errorCount) {
+ if (grunt.fail.errorcount) {
return true;
}
@@ -249,17 +249,18 @@ exports = function(grunt) {
return false;
}
- // update catalog
- saveCatalog(catalog);
- }
+ return catalog;
+ });
grunt.registerMultiTask('frontend', 'Builds font-end part of your web-site: compiles CSS and JS files', function() {
- if (this.target == 'css') {
- return compileCSSTask(this);
- }
+ grunt.log.writeln('Compiling ' + this.target.toUpperCase());
- if (this.target == 'js') {
- return compileJSTask(this);
+ var catalog = grunt.helper('frontend-' + this.target, this.data, getConfig(this), loadCatalog());
+ if (catalog) {
+ // update catalog
+ return saveCatalog(catalog);
}
+
+ return false;
});
};
View
57 test/frontend.js
@@ -2,22 +2,59 @@
var path = require('path');
var csso = require('csso');
+var grunt = require('grunt');
var compileCSSFile = require('../lib/css').compileCSSFile;
-exports.cssCompiler = function(test) {
- var pathResolver = function(file, originalFile) {
- var dirname = originalFile ? path.dirname(originalFile) : __dirname;
- if (file.charAt(0) == '/') {
- // resolve absolute file include
- file = file.replace(/^\/+/, '');
- dirname = __dirname;
- }
- return path.resolve(dirname, file);
- };
+function pathResolver(file, originalFile) {
+ var dirname = originalFile ? path.dirname(originalFile) : __dirname;
+ if (file.charAt(0) == '/') {
+ // resolve absolute file include
+ file = file.replace(/^\/+/, '');
+ dirname = __dirname;
+ }
+ return path.resolve(dirname, file);
+}
+exports.cssCompiler = function(test) {
var compiledCSS = compileCSSFile(pathResolver('css/test.css'), pathResolver);
test.ok(compiledCSS.length > 0, 'Got compiled CSS');
test.ok(!/@import/.test(compiledCSS), 'No imports');
test.done();
};
+
+var config = {
+ webroot: path.join(__dirname, 'out'),
+ srcWebroot: __dirname
+};
+
+exports.testGrunt = {
+ css: function(test) {
+ var payload = {
+ src: pathResolver('css'),
+ dest: pathResolver('out/css')
+ };
+ var catalog = grunt.helper('frontend-css', payload, config);
+
+ test.ok(catalog, 'CSS compiled successfully');
+ test.ok('/css/test-utf.css' in catalog, 'Has test-utf.css');
+ test.ok('/css/test.css' in catalog, 'Has test.css');
+
+ test.done();
+ },
+
+ js: function(test) {
+ var payload = {
+ 'test/out/js/f.js': [
+ 'js/file1.js',
+ 'js/file2.js'
+ ]
+ };
+ var catalog = grunt.helper('frontend-js', payload, config);
+
+ test.ok(catalog, 'JS compiled successfully');
+ test.ok('/js/f.js' in catalog, 'Has f.js');
+
+ test.done();
+ }
+};

0 comments on commit cc5d8fd

Please sign in to comment.