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'), var path = require('path');
parserlib = require('parserlib');
module.exports = function (grunt) { module.exports = function (grunt) {
@ -11,12 +10,9 @@ grunt.initConfig({
// -- Constants ------------------------------------------------------------ // -- Constants ------------------------------------------------------------
NORMALIZE_LIB: path.join(process.cwd(), '../', 'normalize.css'), BUILD_COMMENT: 'THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT!',
BASE_DIR : 'src/base/',
PREFIX : '.k',
COMMENT: '/* THIS FILE IS GENERATED BY A BUILD SCRIPT - DO NOT EDIT! */\n', NORMALIZE_LICENSE: [
LICENSE: [
'/*! normalize.css v1.1.1 | MIT License | git.io/normalize */', '/*! normalize.css v1.1.1 | MIT License | git.io/normalize */',
'/*! Copyright (c) Nicolas Gallagher and Jonathan Neal */', '/*! Copyright (c) Nicolas Gallagher and Jonathan Neal */',
'\n' '\n'
@ -25,58 +21,152 @@ grunt.initConfig({
// -- Clean Config --------------------------------------------------------- // -- Clean Config ---------------------------------------------------------
clean: { clean: {
build: ['build/'], build : ['build/'],
base : ['src/base/css/', 'src/base/tests/', 'src/base/LICENSE.md'] 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 Config --------------------------------------------------------
cssmin: { cssmin: {
options: { options: {
report: 'gzip' // report: 'gzip'
}, },
base: { files: {
files: { expand: true,
'build/base/base-min.css' : ['src/base/css/normalize.css'], src : 'build/*.css',
'build/base/base-context-min.css': ['src/base/css/normalize-context.css'] ext : '-min.css'
} }
}, },
forms: { // -- Contextualize Config -------------------------------------------------
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']
}
},
grids: { contextualize: {
files: { normalize: {
'build/grids/grids-min.css' : ['src/grids/css/*.css'], src : 'src/base/css/normalize.css',
'build/grids/grids-nr-min.css': ['src/grids/css/cssgrids-base.css', dest: 'src/base/css/normalize-context.css',
'src/grids/css/cssgrids-units.css']
}
},
menus: { options: {
files: { prefix: '.k',
'build/menus/menus-min.css' : ['src/menus/css/*.css'], banner: '/* <%= BUILD_COMMENT %> */\n<%= NORMALIZE_LICENSE %>'
'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']
} }
} }
} }
@ -85,152 +175,106 @@ grunt.initConfig({
// -- Main Tasks --------------------------------------------------------------- // -- Main Tasks ---------------------------------------------------------------
grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.registerTask('default', [ grunt.registerTask('default', [
'clean:build', 'clean:build',
'cssmin' 'copy:build',
'concat:build',
'clean:build_res',
'cssmin',
'concat:kimono'
]); ]);
grunt.registerTask('base', ['cssmin:base']); grunt.registerTask('import', [
grunt.registerTask('buttons', ['cssmin:buttons']); 'import-normalize'
grunt.registerTask('forms', ['cssmin:forms']); ]);
grunt.registerTask('grids', ['cssmin:grids']);
grunt.registerTask('menus', ['cssmin:menus']);
grunt.registerTask('tables', ['cssmin:tables']);
// -- Import Tasks ------------------------------------------------------------- // -- Import Tasks -------------------------------------------------------------
grunt.registerTask('base-import-css', 'Import Normalize CSS Files', function () { grunt.registerTask('import-normalize', [
var file = 'normalize.css', 'clean:base',
src = path.join(grunt.config('NORMALIZE_LIB'), file), 'copy:normalize',
dest = path.join(grunt.config('BASE_DIR'), 'css', file); 'contextualize:normalize'
]);
if (!grunt.file.exists(src)) { // -- Contextualize Task -------------------------------------------------------
grunt.fail.fatal('Did you clone normalize.css yet?');
grunt.registerMultiTask('contextualize', 'Makes Contextualized CSS files.', function () {
var Parser = require('parserlib').css.Parser,
done = this.async(),
options = this.options({banner: ''}),
banner = grunt.template.process(options.banner),
processing = 0;
function oneDone() {
if (!(processing -= 1)) {
done();
}
} }
grunt.log.writeln('Copying: '.green + file.cyan + ' to ' + dest.cyan); this.files.forEach(function (filePair) {
grunt.file.write(dest, grunt.config('COMMENT') + grunt.file.read(src)); filePair.src.forEach(function (file) {
}); var src = grunt.file.read(file),
contextual = banner,
parser = new Parser();
grunt.registerTask('base-import-tests', 'Import Normalize Tests', function () { parser.addListener('endstylesheet', function () {
var file = 'test.html', grunt.file.write(filePair.dest, contextual);
src = path.join(grunt.config('NORMALIZE_LIB'), file), grunt.log.writeln('File "' + filePair.dest + '" created.');
dest = path.join(grunt.config('BASE_DIR'), 'tests', 'manual', file); oneDone();
});
if (!grunt.file.exists(src)) { // Fired right before CSS properties are parsed for a certain rule.
grunt.fail.fatal('Did you clone normalize.css yet?'); // Go through and add all the selectors to the `css` string.
} parser.addListener('startrule', function (event) {
var prefix = options.prefix;
grunt.log.writeln('Copying: '.green + file.cyan + ' to ' + dest.cyan); event.selectors.forEach(function (selector, i) {
grunt.file.copy(src, dest); var nextSelector = event.selectors[i + 1];
});
grunt.registerTask('base-import-meta', 'Import Normalize License', function () { // If the selector does not contain the html selector, we
var file = 'LICENSE.md', // can go ahead and prepend .k in front of it.
src = path.join(grunt.config('NORMALIZE_LIB'), file), if (selector.text.indexOf('html') === -1) {
dest = path.join(grunt.config('BASE_DIR'), file); 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']`.
contextual += selector.text.replace('html', prefix).replace(/ +/g, ' ');
}
if (!grunt.file.exists(src)) { // If theres another selector, add a comma.
grunt.fail.fatal('Did you clone normalize.css yet?'); if (nextSelector) {
} contextual += ',\n';
} else {
// Otherwise, add an opening bracket for properties
contextual += ' {\n';
}
});
});
grunt.log.writeln('Copying: '.green + file.cyan + ' to ' + dest.cyan); // Fired right after CSS properties are parsed for a certain rule.
grunt.file.copy(src, dest); // Add the closing bracket to end the CSS Rule.
}); parser.addListener('endrule', function (event) {
contextual += '}\n';
});
grunt.registerTask('base-create-context', 'Make context version', function () { // Fired for each property that the parser encounters. Add these
var context = grunt.config('COMMENT') + grunt.config('LICENSE'), // properties to the `css` string with 4 spaces.
done = this.async(), parser.addListener('property', function (event) {
parser = new parserlib.css.Parser(), // Add 4 spaces tab.
raw = grunt.file.read(grunt.config('BASE_DIR') + 'css/normalize.css'); contextual += ' ' + event.property + ': ' + event.value + ';\n';
});
parser.addListener('startstylesheet', function () { // Do the parsing.
grunt.log.ok('Starting to parse style sheet...'); processing += 1;
}); parser.parse(src);
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();
});
// 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');
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 (selector.text.indexOf('html') === -1) {
context += 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 theres another selector, add a comma.
if (nextSelector) {
context += ',\n';
} else {
// Otherwise, add an opening bracket for properties
context += ' {\n';
}
}); });
}); });
// 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';
});
// 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';
});
// Do the parsing.
parser.parse(raw);
}); });
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", "grunt": "~0.4.1",
"parserlib": "~0.2.2", "parserlib": "~0.2.2",
"grunt-contrib-cssmin": "~0.6.0", "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"
} }
} }