Skip to content
Browse files

support for individually compiling srcFiles.

when a literal *.{ext} is used in the dest, the task detects that it
needs to individually process and compile each file to dest directory.
with the use of basePath and flatten the resulting folder stucture can
be easily adjusted like our copy and compress tasks. closes #1.
  • Loading branch information...
1 parent b90bb6c commit 7104166f1501c94b03323cc5f0f60506c71f7d4c @ctalkington ctalkington committed Sep 23, 2012
View
29 README.md
@@ -35,13 +35,21 @@ This controls how this task (and its helpers) operate and should contain key:val
#### Options
+##### basePath ```string```
+
+This option adjusts the folder structure when compiled to the destination directory. When not explicitly set, best effort is made to locate the basePath by comparing all source filepaths left to right for a common pattern.
+
##### compress ```boolean```
-This specifies if we should compress the compiled css.
+This option specifies if we should compress the compiled css.
+
+##### flatten ```boolean```
+
+This option performs a flat compile that dumps all the files into the root of the destination directory, overwriting files if they exist.
##### paths ```string|array```
-This specifies directories to scan for @import directives when parsing.
+This option specifies directories to scan for @import directives when parsing.
#### Config Example
@@ -53,14 +61,25 @@ stylus: {
paths: ['path/to/import', 'another/to/import']
},
files: {
- 'path/to/result.css': 'path/to/source.styl',
- 'path/to/another.css': ['path/to/sources/*.styl', 'path/to/more/*.style'],
+ 'path/to/result.css': 'path/to/source.styl', // 1:1 compile
+ 'path/to/another.css': ['path/to/sources/*.styl', 'path/to/more/*.style'], // compile and concat into single file
+ 'path/to/*.css': ['path/to/sources/*.styl', 'path/to/more/*.styl'] // compile individually into dest, maintaining folder structure
+ }
+ },
+ flatten: {
+ options: {
+ flatten: true,
+ paths: ['path/to/import', 'another/to/import']
+ },
+ files: {
+ 'path/to/*.css': ['path/to/sources/*.styl', 'path/to/more/*.styl'] // compile individually into dest, flattening folder structure
}
}
}
```
## Release History
+* 2012/09/17 - v0.2.3 - added ability to compile individually into dest, maintaining folder structure.
* 2012/09/17 - v0.2.2 - tests refactored. better watch integration.
-* 2012/08/10 - v0.2.0 - Refactored from grunt-contrib into individual repo.
+* 2012/09/10 - v0.2.0 - refactored from grunt-contrib into individual repo.
View
157 grunt.js
@@ -1,74 +1,85 @@
-/*
- * grunt-contrib-stylus
- * http://gruntjs.com/
- *
- * Copyright (c) 2012 Eric Woroshow, contributors
- * Licensed under the MIT license.
- * https://github.com/gruntjs/grunt-contrib-stylus/blob/master/LICENSE-MIT
- */
-
-module.exports = function(grunt) {
- 'use strict';
-
- // Project configuration.
- grunt.initConfig({
- lint: {
- all: ['grunt.js', 'tasks/*.js', '<config:nodeunit.tasks>']
- },
-
- jshint: {
- options: {
- curly: true,
- eqeqeq: true,
- immed: true,
- latedef: true,
- newcap: true,
- noarg: true,
- sub: true,
- undef: true,
- boss: true,
- eqnull: true,
- node: true,
- es5: true
- }
- },
-
- // Before generating any new files, remove any previously-created files.
- clean: {
- test: ['tmp']
- },
-
- // Configuration to be run (and then tested).
- stylus: {
- compile: {
- files: {
- 'tmp/stylus.css': ['test/fixtures/stylus.styl'],
- 'tmp/stylus_b.css': ['test/fixtures/stylus.styl', 'test/fixtures/stylus2.styl']
- },
- options: {
- paths: ['test/fixtures/include'],
- compress: true
- }
- }
- },
-
- // Unit tests.
- nodeunit: {
- tasks: ['test/*_test.js']
- }
- });
-
- // Actually load this plugin's task(s).
- grunt.loadTasks('tasks');
-
- // The clean plugin helps in testing.
- grunt.loadNpmTasks('grunt-contrib-clean');
-
- // Whenever the 'test' task is run, first clean the 'tmp' dir, then run this
- // plugin's task(s), then test the result.
- grunt.renameTask('test', 'nodeunit');
- grunt.registerTask('test', 'clean stylus nodeunit');
-
- // By default, lint and run all tests.
- grunt.registerTask('default', 'lint test');
+/*
+ * grunt-contrib-stylus
+ * http://gruntjs.com/
+ *
+ * Copyright (c) 2012 Eric Woroshow, contributors
+ * Licensed under the MIT license.
+ * https://github.com/gruntjs/grunt-contrib-stylus/blob/master/LICENSE-MIT
+ */
+
+module.exports = function(grunt) {
+ 'use strict';
+
+ // Project configuration.
+ grunt.initConfig({
+ lint: {
+ all: ['grunt.js', 'tasks/*.js', '<config:nodeunit.tasks>']
+ },
+
+ jshint: {
+ options: {
+ curly: true,
+ eqeqeq: true,
+ immed: true,
+ latedef: true,
+ newcap: true,
+ noarg: true,
+ sub: true,
+ undef: true,
+ boss: true,
+ eqnull: true,
+ node: true,
+ es5: true
+ }
+ },
+
+ // Before generating any new files, remove any previously-created files.
+ clean: {
+ test: ['tmp']
+ },
+
+ // Configuration to be run (and then tested).
+ stylus: {
+ compile: {
+ files: {
+ 'tmp/stylus.css': ['test/fixtures/stylus.styl'],
+ 'tmp/concat.css': ['test/fixtures/stylus.styl', 'test/fixtures/stylus2.styl'],
+ 'tmp/individual/*.css': ['test/fixtures/*.styl', 'test/fixtures/level2/*.styl']
+ },
+ options: {
+ paths: ['test/fixtures/include'],
+ compress: true
+ }
+ },
+ flatten: {
+ files: {
+ 'tmp/individual_flatten/*.css': ['test/fixtures/*.styl', 'test/fixtures/level2/*.styl']
+ },
+ options: {
+ paths: ['test/fixtures/include'],
+ compress: true,
+ flatten: true
+ }
+ }
+ },
+
+ // Unit tests.
+ nodeunit: {
+ tasks: ['test/*_test.js']
+ }
+ });
+
+ // Actually load this plugin's task(s).
+ grunt.loadTasks('tasks');
+
+ // The clean plugin helps in testing.
+ grunt.loadNpmTasks('grunt-contrib-clean');
+
+ // Whenever the 'test' task is run, first clean the 'tmp' dir, then run this
+ // plugin's task(s), then test the result.
+ grunt.renameTask('test', 'nodeunit');
+ grunt.registerTask('test', 'clean stylus nodeunit');
+
+ // By default, lint and run all tests.
+ grunt.registerTask('default', 'lint test');
};
View
2 package.json
@@ -1,7 +1,7 @@
{
"name": "grunt-contrib-stylus",
"description": "Compile LESS files to CSS.",
- "version": "0.2.2",
+ "version": "0.2.3-alpha",
"homepage": "https://github.com/gruntjs/grunt-contrib-stylus",
"author": {
"name": "Eric Woroshow",
View
133 tasks/stylus.js
@@ -13,12 +13,23 @@ module.exports = function(grunt) {
// TODO: ditch this when grunt v0.4 is released
grunt.util = grunt.util || grunt.utils;
- var _ = grunt.util._;
+ var path = require('path');
+
+ // TODO: remove if/when we officially drop node <= 0.7.9
+ path.sep = path.sep || path.normalize('/');
grunt.registerMultiTask('stylus', 'Compile Stylus files into CSS', function() {
var async = grunt.util.async;
var helpers = require('grunt-contrib-lib').init(grunt);
- var options = helpers.options(this);
+ var options = helpers.options(this, {
+ basePath: false,
+ flatten: false
+ });
+
+ if (options.basePath) {
+ options.basePath = path.normalize(options.basePath);
+ options.basePath = grunt.util._(options.basePath).trim(path.sep);
+ }
grunt.verbose.writeflags(options, 'Options');
@@ -27,55 +38,131 @@ module.exports = function(grunt) {
var done = this.async();
+ var basePath;
+ var destType;
+ var newFileDest;
+
var srcFiles;
- var sourceCode;
- var helperOptions;
async.forEachSeries(this.files, function(file, next) {
srcFiles = grunt.file.expandFiles(file.src);
- async.concatSeries(srcFiles, function(srcFile, nextConcat) {
- helperOptions = _.extend({filename: srcFile}, options);
- sourceCode = grunt.file.read(srcFile);
+ if (srcFiles.length === 0) {
+ grunt.fail.warn('Unable to compile; no valid source files were found.');
+ }
- compileStylus(sourceCode, helperOptions, function(css, err) {
- if(!err) {
- nextConcat(null, css);
- } else {
- done();
- }
- });
- }, function(err, css) {
- grunt.file.write(file.dest, css.join('\n') || '');
- grunt.log.writeln('File ' + file.dest + ' created.');
+ destType = detectDestType(file.dest);
+
+ if (destType === 'individual') {
+ basePath = options.basePath || findBasePath(srcFiles);
+
+ async.forEachSeries(srcFiles, function(srcFile, nextFile) {
+ newFileDest = getNewFileDest(file.dest, srcFile, basePath, options.flatten);
- next();
- });
+ compileStylus(srcFile, options, function(css, err) {
+ if(!err) {
+ grunt.file.write(newFileDest, css || '');
+ grunt.log.writeln('File ' + newFileDest + ' created.');
+
+ nextFile(null);
+ } else {
+ done();
+ }
+ });
+ }, function(err) {
+ next();
+ });
+ } else {
+ async.concatSeries(srcFiles, function(srcFile, nextConcat) {
+ compileStylus(srcFile, options, function(css, err) {
+ if(!err) {
+ nextConcat(null, css);
+ } else {
+ done();
+ }
+ });
+ }, function(err, css) {
+ grunt.file.write(file.dest, css.join('\n') || '');
+ grunt.log.writeln('File ' + file.dest + ' created.');
+
+ next();
+ });
+ }
}, function() {
done();
});
});
- var compileStylus = function(source, options, callback) {
- var s = require('stylus')(source);
+ var compileStylus = function(srcFile, options, callback) {
+ options = grunt.util._.extend({filename: srcFile}, options);
+ delete options.basePath;
+ delete options.flatten;
+
+ var srcCode = grunt.file.read(srcFile);
+ var s = require('stylus')(srcCode);
- // load nib if available
try {
s.use(require('nib')());
} catch (e) {}
- _.each(options, function(value, key) {
+ grunt.util._.each(options, function(value, key) {
s.set(key, value);
});
s.render(function(err, css) {
if (err) {
grunt.log.error(err);
grunt.fail.warn('Stylus failed to compile.');
+
callback(css, true);
} else {
callback(css, null);
}
});
};
+
+ var detectDestType = function(dest) {
+ var destFile = path.basename(dest);
+
+ if (grunt.util._.startsWith(destFile, '*')) {
+ return 'individual';
+ } else {
+ return 'single';
+ }
+ };
+
+ var getNewFileDest = function(dest, srcFile, basePath, flatten) {
+ srcFile = path.normalize(srcFile);
+
+ var newDest = path.dirname(dest);
+ var newName = path.basename(srcFile, path.extname(srcFile)) + path.extname(dest);
+ var relative = path.dirname(srcFile);
+
+ if (flatten) {
+ relative = '';
+ } else if (basePath && basePath.length > 1) {
+ relative = grunt.util._(relative).chain().strRight(basePath).trim(path.sep).value();
+ }
+
+ // make paths outside grunts working dir relative
+ relative = relative.replace(/\.\.(\/|\\)/g, '');
+
+ return path.join(newDest, relative, newName);
+ };
+
+ var findBasePath = function(srcFiles) {
+ var basePaths = [];
+ var dirName;
+
+ srcFiles.forEach(function(srcFile) {
+ srcFile = path.normalize(srcFile);
+ dirName = path.dirname(srcFile);
+
+ basePaths.push(dirName.split(path.sep));
+ });
+
+ basePaths = grunt.util._.intersection.apply([], basePaths);
+
+ return path.join.apply(path, basePaths);
+ };
};
View
0 test/expected/stylus_b.css → test/expected/concat.css
File renamed without changes.
View
1 test/expected/individual/level2/stylus3.css
@@ -0,0 +1 @@
+body{font:Helvetica;font-size:10px}
View
1 test/expected/individual/stylus.css
@@ -0,0 +1 @@
+body{font:Helvetica;font-size:10px}
View
1 test/expected/individual/stylus2.css
@@ -0,0 +1 @@
+#header{font:Helvetica}
View
1 test/expected/individual_flatten/stylus.css
@@ -0,0 +1 @@
+body{font:Helvetica;font-size:10px}
View
1 test/expected/individual_flatten/stylus2.css
@@ -0,0 +1 @@
+#header{font:Helvetica}
View
1 test/expected/individual_flatten/stylus3.css
@@ -0,0 +1 @@
+body{font:Helvetica;font-size:10px}
View
4 test/fixtures/level2/stylus3.styl
@@ -0,0 +1,4 @@
+@import "variables.styl"
+body
+ font $font-name
+ font-size 10px
View
22 test/stylus_test.js
@@ -1,19 +1,35 @@
var grunt = require('grunt');
+var fs = require('fs');
exports.stylus = {
compile: function(test) {
'use strict';
- test.expect(2);
+ test.expect(3);
var actual = grunt.file.read('tmp/stylus.css');
var expected = grunt.file.read('test/expected/stylus.css');
test.equal(expected, actual, 'should compile stylus to css, handling includes and compression');
- actual = grunt.file.read('tmp/stylus_b.css');
- expected = grunt.file.read('test/expected/stylus_b.css');
+ actual = grunt.file.read('tmp/concat.css');
+ expected = grunt.file.read('test/expected/concat.css');
test.equal(expected, actual, 'should concat output when passed an array');
+ actual = fs.readdirSync('tmp/individual').sort();
+ expected = fs.readdirSync('test/expected/individual').sort();
+ test.deepEqual(expected, actual, 'should individually compile files');
+
+ test.done();
+ },
+ flatten: function(test) {
+ 'use strict';
+
+ test.expect(1);
+
+ var actual = fs.readdirSync('tmp/individual_flatten').sort();
+ var expected = fs.readdirSync('test/expected/individual_flatten').sort();
+ test.deepEqual(expected, actual, 'should individually compile files (to flat structure)');
+
test.done();
}
};

0 comments on commit 7104166

Please sign in to comment.
Something went wrong with that request. Please try again.