Revamp Gruntfile to improve its build and import tasks

This is a major rewrite of the Gruntfile which uses a config-driven
approach and leverages grunt-contrib-* plugins to do the heavy lifting.

This creates the build files according to the following conventions:

* `-nr.css`: Rollup `-core.css` + `[module].css` + `-[feature].css`.
* `[module].css`: Rollup `-nr.css`  + `-r.css`.
* `-min.css`: A minified file version of the files of the same name.
* `kimono-min.css`: Rollup of all `[module].css` files.
* `kimono-nr-min.css`: Rollup of all modules without `@media`

**Note:** `-r.css` files are not maintained as separate files.
This commit is contained in:
Eric Ferraiuolo 2013-05-14 13:12:01 -04:00
parent 8ed6826eb6
commit 79306853bb
2 changed files with 219 additions and 173 deletions

View File

@ -1,5 +1,4 @@
var path = require('path'),
parserlib = require('parserlib');
var path = require('path');
module.exports = function (grunt) {
@ -11,12 +10,9 @@ grunt.initConfig({
// -- Constants ------------------------------------------------------------
NORMALIZE_LIB: path.join(process.cwd(), '../', 'normalize.css'),
BASE_DIR : 'src/base/',
PREFIX : '.k',
BUILD_COMMENT: 'THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT!',
COMMENT: '/* THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT! */\n',
LICENSE: [
NORMALIZE_LICENSE: [
'/*! normalize.css v1.1.1 | MIT License | git.io/normalize */',
'/*! Copyright (c) Nicolas Gallagher and Jonathan Neal */',
'\n'
@ -26,57 +22,151 @@ grunt.initConfig({
clean: {
build : ['build/'],
build_res: ['build/*-r.css'],
base : ['src/base/css/', 'src/base/tests/', 'src/base/LICENSE.md']
},
// -- Copy Config ----------------------------------------------------------
copy: {
build: {
expand : true,
flatten: true,
src : 'src/**/css/*.css',
dest : 'build/',
rename: function (dest, src) {
// normalize -> base
src = src.replace(/^normalize(-.+\.css|\.css)$/, 'base$1');
return path.join(dest, src);
}
},
normalize: {
expand : true,
flatten: true,
cwd : '../normalize.css/',
src : '{LICENSE.md,normalize.css,test.html}',
dest : 'src/base/',
rename: function (dest, file) {
if (grunt.file.isMatch('*.css', file)) {
return path.join(dest, 'css', file);
}
if (grunt.file.isMatch('*.html', file)) {
return path.join(dest, 'tests', 'manual', file);
}
return path.join(dest, file);
},
options: {
processContent: function (content, file) {
var comment = grunt.config('BUILD_COMMENT');
if (grunt.file.isMatch({matchBase: true}, '*.css', file)) {
content = '/* ' + comment + ' */\n' + content;
} else if (grunt.file.isMatch({matchBase: true}, '*.html', file)) {
content = '<!-- ' + comment + ' -->\n' + content;
}
return content;
}
}
}
},
// -- Concat Config --------------------------------------------------------
concat: {
build: {
files: [
{'build/buttons.css': [
'build/buttons-core.css',
'build/buttons.css'
]},
{'build/forms-nr.css': [
'build/forms-core.css',
'build/forms.css'
]},
{'build/forms.css': [
'build/forms-nr.css',
'build/forms-r.css'
]},
{'build/grids-nr.css': [
'build/grids-base.css',
'build/grids-units.css'
]},
{'build/grids.css': [
'build/grids-nr.css',
'build/grids-r.css'
]},
{'build/menus-nr.css': [
'build/menus-core.css',
'build/menus.css',
'build/menus-paginator.css'
]},
{'build/menus.css': [
'build/menus-nr.css',
'build/menus-r.css'
]}
]
},
kimono: {
files: {
'build/kimono-min.css': [
'build/base-min.css',
'build/buttons-min.css',
'build/forms-min.css',
'build/grids-min.css',
'build/menus-min.css',
'build/tables-min.css'
],
'build/kimono-nr-min.css': [
'build/base-min.css',
'build/buttons-min.css',
'build/forms-nr-min.css',
'build/grids-nr-min.css',
'build/menus-nr-min.css',
'build/tables-min.css'
]
}
}
},
// -- CSSMin Config --------------------------------------------------------
cssmin: {
options: {
report: 'gzip'
// report: 'gzip'
},
base: {
files: {
'build/base/base-min.css' : ['src/base/css/normalize.css'],
'build/base/base-context-min.css': ['src/base/css/normalize-context.css']
expand: true,
src : 'build/*.css',
ext : '-min.css'
}
},
forms: {
files: {
'build/forms/forms-min.css' : ['src/forms/css/*.css'],
'build/forms/forms-nr-min.css': ['src/forms/css/forms-core.css',
'src/forms/css/forms.css']
}
},
// -- Contextualize Config -------------------------------------------------
grids: {
files: {
'build/grids/grids-min.css' : ['src/grids/css/*.css'],
'build/grids/grids-nr-min.css': ['src/grids/css/cssgrids-base.css',
'src/grids/css/cssgrids-units.css']
}
},
contextualize: {
normalize: {
src : 'src/base/css/normalize.css',
dest: 'src/base/css/normalize-context.css',
menus: {
files: {
'build/menus/menus-min.css' : ['src/menus/css/*.css'],
'build/menus/menus-nr-min.css': ['src/menus/css/list-core.css',
'src/menus/css/list.css',
'src/menus/css/list-paginator.css']
}
},
tables: {
files: {
'build/tables/tables-min.css': ['src/tables/css/*.css']
}
},
buttons: {
files: {
'build/buttons/buttons-min.css': ['src/buttons/css/*.css']
options: {
prefix: '.k',
banner: '/* <%= BUILD_COMMENT %> */\n<%= NORMALIZE_LICENSE %>'
}
}
}
@ -85,152 +175,106 @@ grunt.initConfig({
// -- Main Tasks ---------------------------------------------------------------
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.registerTask('default', [
'clean:build',
'cssmin'
'copy:build',
'concat:build',
'clean:build_res',
'cssmin',
'concat:kimono'
]);
grunt.registerTask('base', ['cssmin:base']);
grunt.registerTask('buttons', ['cssmin:buttons']);
grunt.registerTask('forms', ['cssmin:forms']);
grunt.registerTask('grids', ['cssmin:grids']);
grunt.registerTask('menus', ['cssmin:menus']);
grunt.registerTask('tables', ['cssmin:tables']);
grunt.registerTask('import', [
'import-normalize'
]);
// -- Import Tasks -------------------------------------------------------------
grunt.registerTask('base-import-css', 'Import Normalize CSS Files', function () {
var file = 'normalize.css',
src = path.join(grunt.config('NORMALIZE_LIB'), file),
dest = path.join(grunt.config('BASE_DIR'), 'css', file);
grunt.registerTask('import-normalize', [
'clean:base',
'copy:normalize',
'contextualize:normalize'
]);
if (!grunt.file.exists(src)) {
grunt.fail.fatal('Did you clone normalize.css yet?');
}
// -- Contextualize Task -------------------------------------------------------
grunt.log.writeln('Copying: '.green + file.cyan + ' to ' + dest.cyan);
grunt.file.write(dest, grunt.config('COMMENT') + grunt.file.read(src));
});
grunt.registerTask('base-import-tests', 'Import Normalize Tests', function () {
var file = 'test.html',
src = path.join(grunt.config('NORMALIZE_LIB'), file),
dest = path.join(grunt.config('BASE_DIR'), 'tests', 'manual', file);
if (!grunt.file.exists(src)) {
grunt.fail.fatal('Did you clone normalize.css yet?');
}
grunt.log.writeln('Copying: '.green + file.cyan + ' to ' + dest.cyan);
grunt.file.copy(src, dest);
});
grunt.registerTask('base-import-meta', 'Import Normalize License', function () {
var file = 'LICENSE.md',
src = path.join(grunt.config('NORMALIZE_LIB'), file),
dest = path.join(grunt.config('BASE_DIR'), file);
if (!grunt.file.exists(src)) {
grunt.fail.fatal('Did you clone normalize.css yet?');
}
grunt.log.writeln('Copying: '.green + file.cyan + ' to ' + dest.cyan);
grunt.file.copy(src, dest);
});
grunt.registerTask('base-create-context', 'Make context version', function () {
var context = grunt.config('COMMENT') + grunt.config('LICENSE'),
grunt.registerMultiTask('contextualize', 'Makes Contextualized CSS files.', function () {
var Parser = require('parserlib').css.Parser,
done = this.async(),
parser = new parserlib.css.Parser(),
raw = grunt.file.read(grunt.config('BASE_DIR') + 'css/normalize.css');
options = this.options({banner: ''}),
banner = grunt.template.process(options.banner),
processing = 0;
parser.addListener('startstylesheet', function () {
grunt.log.ok('Starting to parse style sheet...');
});
function oneDone() {
if (!(processing -= 1)) {
done();
}
}
this.files.forEach(function (filePair) {
filePair.src.forEach(function (file) {
var src = grunt.file.read(file),
contextual = banner,
parser = new Parser();
parser.addListener('endstylesheet', function () {
var contextFile = path.join(grunt.config('BASE_DIR'), 'css', 'normalize-context.css');
grunt.log.ok('Finished parsing style sheet...');
grunt.file.write(contextFile, context);
grunt.log.ok('Done creating context build!');
done();
grunt.file.write(filePair.dest, contextual);
grunt.log.writeln('File "' + filePair.dest + '" created.');
oneDone();
});
// Fired right before CSS properties are parsed for a certain rule. Go
// through and add all the selectors to the `css` string.
// Fired right before CSS properties are parsed for a certain rule.
// Go through and add all the selectors to the `css` string.
parser.addListener('startrule', function (event) {
var prefix = grunt.config('PREFIX');
var prefix = options.prefix;
event.selectors.forEach(function (selector, i) {
var nextSelector = event.selectors[i + 1];
// If the selector does not contain the html selector, we can go
// ahead and prepend .k in front of it.
// If the selector does not contain the html selector, we
// can go ahead and prepend .k in front of it.
if (selector.text.indexOf('html') === -1) {
context += prefix + ' ' + selector.text;
contextual += prefix + ' ' + selector.text;
} else if (selector.text.indexOf('html') !== -1) {
// If it contains `html`, replace the `html` with `.k`. Replace
// multiple spaces with a single space. This is for the case
// where `html input[type='button']` comes through as
// `html input[type='button']`.
context += selector.text.replace('html', prefix).replace(/ +/g, ' ');
// If it contains `html`, replace the `html` with `.k`.
// Replace multiple spaces with a single space. This is
// for the case where `html input[type='button']` comes
// through as `html input[type='button']`.
contextual += selector.text.replace('html', prefix).replace(/ +/g, ' ');
}
// If theres another selector, add a comma.
if (nextSelector) {
context += ',\n';
contextual += ',\n';
} else {
// Otherwise, add an opening bracket for properties
context += ' {\n';
contextual += ' {\n';
}
});
});
// Fired right after CSS properties are parsed for a certain rule. Add the
// closing bracket to end the CSS Rule.
// Fired right after CSS properties are parsed for a certain rule.
// Add the closing bracket to end the CSS Rule.
parser.addListener('endrule', function (event) {
context += '}\n';
contextual += '}\n';
});
// Fired for each property that the parser encounters. Add these properties
// to the `css` string with 4 spaces.
// Fired for each property that the parser encounters. Add these
// properties to the `css` string with 4 spaces.
parser.addListener('property', function (event) {
// Add 4 spaces tab.
context += ' ' + event.property + ': ' + event.value + ';\n';
contextual += ' ' + event.property + ': ' + event.value + ';\n';
});
// Do the parsing.
parser.parse(raw);
processing += 1;
parser.parse(src);
});
});
grunt.registerTask('base-prep', 'Prep Normalize.css import', function () {
var normalize = grunt.config('NORMALIZE_LIB');
grunt.log.write('Looking for Normalize.css'.green);
if (!grunt.file.exists(normalize)) {
grunt.log.writeln('');
grunt.fail.fatal('Could not locate Normalize.css repo: ' + normalize + '\nDid you clone it as a sibling of the Kimono');
}
grunt.log.writeln('...OK'.white);
});
grunt.registerTask('base-all', [
'base-prep',
'clean:base',
'base-import',
'base-create-context'
]);
grunt.registerTask('base-import', [
'base-import-css',
'base-import-tests',
'base-import-meta'
]);
};

View File

@ -6,6 +6,8 @@
"grunt": "~0.4.1",
"parserlib": "~0.2.2",
"grunt-contrib-cssmin": "~0.6.0",
"grunt-contrib-clean": "~0.4.1"
"grunt-contrib-clean": "~0.4.1",
"grunt-contrib-copy": "~0.4.1",
"grunt-contrib-concat": "~0.3.0"
}
}