Stylus 0.15.1 - new property reference & @keyframes fabrication features
Stylus 0.15.x adds a bunch of cool new functionality as well as some important bug fixes.
@keyframes fabrication
The new keyframes fabrication support automatically duplicates your @keyframes definitions to support vendor prefixes. Note that this is only a default, as shown below:
@-moz-keyframes foo {
0% {
color: #000;
}
100% {
color: #fff;
}
}
@-webkit-keyframes foo {
0% {
color: #000;
}
100% {
color: #fff;
}
}
@keyframes foo {
0% {
color: #000;
}
100% {
color: #fff;
}
}
one can tweak this functionality using the vendors list as shown in the following snippet which will only expand to the -webkit- vendor prefix.
vendors = moz official
@keyframes foo {
from {
color: black
}
to {
color: white
}
}
The vendors property may be utilized more in the future, and perhaps in frameworks like “nib” to unify configuration.
Property reference
The syntax @<property> has been introduced to reference values of existing properties. For example if you want to re-use values, you may typically do something like below instead of typing the value several times, also allowing you to use functions with w.
.button
width: w = 50px
height: w
The new property reference syntax allows you to treat properties as variables, which can also be accessed within mixins:
.button
width: 50px
height: @width
Comment compilation
When the compress option is true Stylus will not output single-line nor multi-line comments, which are commonly found used for licensing in stylesheet frameworks etc, so it’s useful to strip these out however you can now force output with the ! character /*!.
New built-in functions
The math functions cos() and sin() were added, as well as exposing PI.
Bug fixes
Connect 1.7.0 - fast static file memory cache and more
Be sure to update Connect to the latest release 1.7.0 for the following goodies!
staticCache() middleware
1.7.0 now provides a new middleware named staticCache(), acting as a memory cache on top of the regular static() middleware. Older versions of Connect used to provide memory caching, however it was baked right into static(), bloating the middleware more than necessary.
Benchmarks
The test file for these benchmarks is a small 4kb readme:
$ du -h Readme.md
4.0K Readme.md
and the following ApacheBench command:
$ ab -n 5000 -c 50 -k http://local/Readme.md
First up we have Connect static(), which does not perform any caching, performing disk I/O each request, serving ~2400rps.
connect()
.use(connect.static(__dirname))
.listen(3000)
Next up we have node-static one of the other popular node solutions, serving ~3800rps
var static = require('node-static')
, file = new static.Server(__dirname);
http.createServer(function(req, res){
file.serve(req, res);
}).listen(3000);
Then we have the new staticCache() middleware paired with static() serving ~5000rps, a 24% increase over node-static (which also performs memory caching), and ~52% over static() alone.
connect()
.use(connect.staticCache())
.use(connect.static(__dirname))
.listen(3000)
staticCache() is configurable, allowing you to specify the maximum number of cache objects to store, and the maximum size allowed, so you can cap resources appropriately. The middleware implements a Least Recently Used algorithm to prioritize popular files, knocking less popular objects out of the cache.
res.headerSent
Node core has a private flag named res._headerSent, allowing you to check if the header has been written or not, this is extremely useful in some cases, so Connect has publicized this property as res.headerSent until Node core does (if ever), using a getter to reference the private property.
logger() immediate option
The logger() middleware now provides { immediate: true }, as by default this middleware will log the response of a request, not when the request is first received, allowing logging of response-times etc.
commander.js - nodejs command-line interfaces made easy
Commander.js is a small node.js module allowing you to define options and interacte with the user’s terminal in a simple and natural way, inspired by the Ruby library of the same name.
Features
- self-documenting code
- auto-generated help
- combined short flags (“-abc” == “-a -b -c”)
- option defaults
- option coercion
- command parsing
- prompts
Example
A basic commander program looks something like the following (taken from serve). It’s extremely easy to see what’s going on, all the options provided by the executable are laid out infront of you.
program
.version('0.0.1')
.option('-p, --port <port>', 'specify the port [3000]', Number, 3000)
.option('-H, --hidden', 'enable hidden file serving')
.option('-I, --no-icons', 'disable file icons')
.option('-L, --no-logs', 'disable request logging')
.parse(process.argv);
In the previous example only --port accepts an argument, and the value of program.port defaults to 3000. The options --no-icons and --no-logs default their properties to true, only when --no-icons is specified will program.icons be false.
The usage information is free!:
$ serve --help
Usage: serve [options]
Options:
-v, --version output the version number
-p, --port <port> specify the port [3000]
-H, --hidden enable hidden file serving
-I, --no-icons disable file icons
-L, --no-logs disable request logging
-h, --help output usage information
Utilities
Commander is bundled with some utilities for prompting user input, confirmations, passwords, lists of choices etc. Most of these utilities will ask for input if the user simply hits enter and should respond.
Below is an example of asking for a name using a single-line input prompt:
program.prompt('name: ', function(name){
console.log('hi %s', name);
});
Multi-line input is easy too, just leave out the trailing space in the message:
program.prompt('description:', function(name){
console.log('hi %s', name);
});
Coercion is useful for dates, numbers etc:
program.prompt('Age: ', Number, function(age){
console.log('age: %j', age);
});
Password prompts masking off input:
program.password('Password: ', function(pass){
console.log('got "%s"', pass);
});
Or providing a mask char:
program.password('Password: ', '*', function(pass){
console.log('got "%s"', pass);
});
Confirmations require “yes” or “y” to result in true:
program.confirm('continue? ', function(ok){
console.log(' got %j', ok);
});
There’s also choice support, so users can select from a list:
var list = ['tobi', 'loki', 'jane', 'manny', 'luna'];
console.log('Choose the coolest pet:');
program.choose(list, function(i){
console.log('you chose %d "%s"', i, list[i]);
});
presenting:
Choose the coolest pet:
1) tobi
2) loki
3) jane
4) manny
5) luna
Commands
Though I haven’t had time to polish them up yet, commander supports the idea of .. well… “commands”. The “root” executable is an instanceof Command, and well you can recursively define these to create a rich interface. GIT is a great example of this, many larger utilities use sub-command such as git remote to accept arguments, and may all then have their own options etc, using the same API as the root command. The following is a simple example:
#!/usr/bin/env node
var program = require('../');
program
.version('0.0.1')
.option('-C, --chdir <path>', 'change the working directory')
.option('-c, --config <path>', 'set config path [./deploy.conf]')
.option('-T, --no-tests', 'ignore test hook')
// $ deploy setup stage
// $ deploy setup
program
.command('setup [env]')
.description('run setup commands for all envs')
.action(function(env){
env = env || 'all';
console.log('setup for %s env(s)', env);
});
// $ deploy stage
// $ deploy production
program
.command('*')
.action(function(env){
console.log('deploying "%s"', env);
});
program.parse(process.argv);
Moar Libraries!
Dont forget to check out these other great CLI-related libraries:
Introducing Texty & Super Agent screencast
In this 20 minute screencast we dive into the full-text Redis search library reds for nodejs, an introduction to the canvas-only text editing library Texty, discussing how you can style canvas drawings with CSS, and the “ajax with less suck” library superagent.
node recap #2
JSONSelect
This first library is JSONSelect. This library allows you to query JSON using css selectors! this seems like an obvious one, but I haven’t personally seen any other implementations.
It’s not node-specific, however it is JavaScript, so naturally it works with nodejs as well, and over all it’s just a neat concept. Suppose we have the following JSON:
JSONSelect
{
"name": {
"first": "Lloyd",
"last": "Hilaiel"
},
"favoriteColor": "yellow",
"languagesSpoken": [
{
"language": "Bulgarian",
"level": "advanced"
},
{
"language": "English",
"level": "native"
},
{
"language": "Spanish",
"level": "beginner"
}
],
"seatingPreference": [
"window",
"aisle"
],
"drinkPreference": [
"whiskey",
"beer",
"wine"
],
"weight": 172
}
To grab the languages “Bulgarian”, “English” and “Spanish” we could issue the following query:
.languagesSpoken .language
cool!
node-canvas Image#src=Buffer
A few days ago I added support to node-canvas for Image#src= to accept Buffer objects, not only path strings. This needs to be polished, and to increase format support but we can finally apply image data from arbitrary sources:
var buf = fs.readFileSync(__dirname + '/textures/jpeg');
var img = new Image;
img.src = buf;
ctx.drawImage(img, 0, 0, 400, 400);
When the Buffer is assigned we can sniff the bytes to determine the format by looking for the “magic number”. For example a PNG starts with the following bytes “89 50 4e 47 0d 0a 1a 0a”, while a JPEG starts with “ff d8”.
Express 2.3.8
A recent Express refactor moved Connect’s router middleware into Express so that I could modify it further. This also means that Connect 2.0 will no longer have router. At first it seemed like a good idea, but routing to specific and it gives people the wrong idea, Connect is meant to be an abstraction layer for higher level frameworks, not to be used directly for application logic. Though this is not true for all cases, such as simply serving static files from a directory or two.
This commit consists of a general refactor of the router, while retaining the public API. What does this mean for Express? flexibility! For example now we may query Express to see which (if any) routes match a specific route path:
app.get('/user/:id');
// => [Route]
or routes that match any HTTP method:
app.all('/user/:id');
// => [Route, Route, Route]
this is functionally equivalent to:
app.lookup.all('/user/:id');
Another alternative is “matching” rather than “lookup”. With app.match.VERB() we can query Express to see which routes would match the url passed, for example:
app.match.get('/user/12/edit');
// => [Route]
app.match.del('/user/12');
// => [Route]
app.match.all('/user/12?foo=bar');
// => [Route, Route]
In this commit I’ve introduced the ability to register logic for defining parameters. Within Express core we only support middleware-style param pre-conditions, for example auto-loading a user for any route that includes the :uid param:
app.param('uid', function(req, res, next, uid){
User.find(uid, function(err, user){
if (err) return next(err);
req.user = user;
next();
});
});
app.get('/user/:uid, function(){
// req.user
});
while this is great, some cases can be less verbose, while still expanding to middleware-style functions, this is where express-params comes in. This plugin extends Express with additional app.param() logic, for example via RegExp:
app.param('uid', /^[0-9]+$/);
app.get('/user/:uid', function(req, res, next){
var uid = req.params.uid;
res.send('user ' + uid);
});
app.get('/user/:name', function(req, res, next){
var name = req.params.name;
res.send('user ' + name);
});
or via return value, useful for validations and coercion:
app.param('id', Number);
app.get('/user/:id', function(req, res, next){
var id = req.params.id;
res.send('typeof ' + typeof id + ' ' + id);
});
Fabric.js
This one is not node related, but I wanted to sneak it in :) Fabric is a slick client-side library providing an object model on top of canvas. It has a built-in drawing mode and path smoothing, SVG parsing and some other really cool features. If you do a lot of canvas work it’s worth checking out, the author did a good job at keeping the source clean and commented, which is very important when utilizing open source.

node-migrate
node-migrate is a very small abstract migration framework. All it cares about is that you supply an up() and down() method, and invoke the callback when your migration is complete:
exports.up = function(next){
next();
};
exports.down = function(next){
next();
};
node-migrate comes with the migrate(1) executable, allowing you to run and create migrations, for example here we add two migrations, populating ./migrations/0-add-pets.js etc.
$ migrate create add-pets
$ migrate create add-owners
The contents of our badass pet adding migration might look something like this:
var db = require('./db');
exports.up = function(next){
db.rpush('pets', 'tobi');
db.rpush('pets', 'loki');
db.rpush('pets', 'jane', next);
};
exports.down = function(next){
db.rpop('pets');
db.rpop('pets', next);
};
Now all we have to do is run the migrations:
$ migrate
up : migrations/0-add-pets.js
up : migrations/1-add-jane.js
up : migrations/2-add-owners.js
up : migrations/3-coolest-pet.js
migration : complete
The next time we run migrate, we’ll see that they have already been completed:
$ migrate
migration : complete
For more information check out the GitHub repo.
Move.js
The other night I noticed that Apple’s new iMac page showcased some really slick CSS3 animations, and wanted a really simple and intuitive way to create similar effects so I came up with Move.js.
Below is an example of the API:
move('#example-13 .box2')
.set('background-color', 'red')
.x(500)
.scale(.5)
.rotate(60)
.then()
.rotate(30)
.scale(1.5)
.set('border-radius', 5)
.set('background-color', 'white')
.then()
.set('opacity', 0)
.pop()
.pop()
.end();
It’s still in it’s early stages, and I don’t have much in the way of documentation yet however feel free to check out the site as well as the examples in the repository.
Stylus vs SASS vs LESS error reporting
I was curious what SASS did as far as error reporting goes, so I tried the following snippet with SASS, LESS as well as Stylus.
body {
form input {
background: foo[fail];
}
}
LESS
The output from LESS was terrible:
Syntax Error on line 4
note: that for LESS I had to tweak the input to “foo[fail’]’;” since it simply consumes the input above
SASS
The SASS output was not bad, it shows you a tiny chunk of the line for context:
Syntax error: Invalid CSS after "...background: foo": expected ";", was "[fail];"
on line 4 of standard input
Use --trace for backtrace.
note: —trace is for the ruby stack trace, not SASS
Stylus
Then we have Stylus, showing you ~8 lines of context (by default), and even a detailed stack trace including the call sites much like you would find in other languages.
Error: /tmp/stylus/test.styl:4
1|
2| body {
3| form input {
> 4| background: foo[fail];
5| }
6| }
7|
cannot perform foo[(fail)]
at "form input" (/tmp/stylus/test.styl:4)
at "body" (/tmp/stylus/test.styl:3)