Node.jsでLESSファイルを動的にコンパイルする
「Less & Sass Advent calendar 2011」9日目のです。
流れをぶった切ってしまってあれなのですが、Node.jsとあわせてLESSファイルを動的にコンパイルするようなやつ書きます。
みなさんご存知の通りLESSはJSで書かれているので、Node.jsを使ったアプリでは動的にLESSファイルをコンパイルできます。
ところがLESSのサイトに書いてある方法は
var parser = new(less.Parser);
parser.parse('.class { width: 1 + 1 }', function (err, tree) {
if (err) { return console.error(err) }
console.log(tree.toCSS());
});
・・・やさしくない!
まあでも早い話、parse(hoge, func)のhogeにLESSファイルの文字列渡せばtreeに結果が入ってくるってことですね。
これはLESSの記事というかNode.jsの記事じゃないのとかそういう細かいことはいいじゃないですか・・・
LESSファイルをコンパイルできるようになるまで
LESSファイルの場所
Expressコマンドで作ったテンプレートを使うことにします。Expressコマンドを実行すると(テンプレエンジンはjqtplにしてます)
$ express -t jqtpl less // less ├ app.js ├ package.json ├ public │ ├ images │ ├ javascripts │ └ stylesheets ├ routes │ └ index.js └ views
みたいにもろもろ出来ます。肝心のLESSファイルは views ディレクトリに less ディレクトリを作ってそこに入れることにしましょう。
// less ├ app.js ├ package.json ├ public │ ├ images │ ├ javascripts │ └ stylesheets ├ routes │ └ index.js └ views ├ tmpl // ここにjqtplテンプレファイル入れる └ less // ここにLESSファイル入れる
less ディレクトリに次の内容の styles.less を作って入れておきます。
.class {
width: 1 + 1px;
}
a {
text-decoration: underline;
&:hover {
text-decoration: none;
}
}
ちなみに、今
http://localhost:3000(ここは環境によります)/less/styles.less
にアクセスしてみると、当然のように表示できません。
LESSのインストール
とりあえず最初から順を追っていきます。まずはnpmを使ってLESSをインストールしておきます。
$ npm install less@latest
LESSをrequireする
この記事ではJSコードは app.js に書いています。インストールしたらLESSが使えるようになるので、LESSをrequireします。ついでに、今回はpathとfsも使っているので一緒にrequireしておきます。
最後に今回使った app.js をそのまま貼ってるので詳しくはそっち見てください。
var less = require('less'),
path = require('path'),
fs = require('fs');
LESSファイルにアクセスしたときのなんやらを設定
まずはファイルにアクセスする前の準備です。
app.get(/^\/less\/.+/, function(req, res) {
// less/hoge にアクセスした場合
var fileRoot = __dirname + '/views/less/', // less ファイルのディレクトリ
extname = path.extname(req.url), // .less の部分が返る
basename = path.basename(req.url), // styles.less の部分が返る
filePath = undefined;
if ( extname !== '.less' ) {
// lessファイルじゃなかったら・・・
console.log('that is not less file');
}
filePath = fileRoot + basename; // パスを取得しておく
// ここに続き書く
});
ファイルパスまで準備できたので、次はファイルを読み込みます。ファイルを読み込めたら、一旦LESSをパースしてCSSへ変換、その後変換したCSSをファイルとして返すようにします。
app.get(......
........
fs.readFile(filePath, 'utf8', function(err, str) {
// str で読み込んだ LESSファイルのバッファを受け取る
var parser = undefined;
if ( err ) {
// 読み込み先でエラーがあったら・・・
console.log('file doesn\'t exist: ' + basename);
}
parser = new(less.Parser);
// LESSのパーサー
parser.parse(str, function(err, tree) {
// LESSファイル の文字列部分を渡す
// tree にパースした LESSのオブジェクトを受け取る
var css = undefined;
if ( err ) {
// パースでエラーがあれば・・・
console.log('parse error: ' + err.message);
}
// toCSS()メソッドを実行するとCSSが返る
css = tree.toCSS();
res.writeHead(200, {
'Content-Type': 'text/css'
});
res.end(css, 'utf-8');
// CSSをファイルとして返す
});
});
});
実行して、アクセスしてみる
http://localhost:3000/less/styles.less にアクセスしてみましょう。
.class {
width: 2px;
}
a {
text-decoration: underline;
}
a:hover {
text-decoration: none;
}
完璧っぽい!
CSSを圧縮しておきたい場合
ちなみに、toCSS()メソッドのオプションで compress 指定をすると圧縮したCSSファイルを返せます。
css = tree.toCSS({ compress: true });
圧縮されたCSSが返ります。ナイス。
.class{width:2px;}
a{text-decoration:underline;}a:hover{text-decoration:none;}
おわり
自分でやってるときはかなり時間かかってしまったんですけど、、、記事にするとかなり短くて凹みました・・・あ、@import とかするとおうふな感じになるので、それはまたどこかで書きます。。
付録:今回使った app.js と HTML(jqtpl)/LESSファイル
app.js は Expressコマンドで出来るやつをベースに使ってます。
app.js
/**
* Module dependencies.
*/
var express = require('express')
, jqtpl = require('jqtpl')
, less = require('less')
, path = require('path')
, fs = require('fs')
, routes = require('./routes')
var app = module.exports = express.createServer();
// Configuration
app.configure(function(){
app.set('views', __dirname + '/views/tmpl');
app.set('view engine', 'html');
app.set('view options', {
layout: false
});
app.register('.html', jqtpl.express);
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
app.configure('development', function(){
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
app.use(express.errorHandler());
});
// Routes
app.get('/', routes.index);
app.get(/^\/less\/.+/, function(req, res) {
var fileRoot = __dirname + '/views/less/',
extname = path.extname(req.url),
basename = path.basename(req.url),
filePath = undefined;
if ( extname !== '.less' ) {
console.log('that is not less file');
}
filePath = fileRoot + basename;
fs.readFile(filePath, 'utf8', function(err, str) {
var parser = undefined;
if ( err ) {
console.log('file doesn\'t exist: ' + basename);
}
parser = new(less.Parser);
parser.parse(str, function(err, tree) {
var css = undefined;
if ( err ) {
console.log('parse error: ' + err.message);
}
css = tree.toCSS({ compress: true });
res.writeHead(200, {
'Content-Type': 'text/css'
});
res.end(css, 'utf-8');
});
});
});
app.listen(3000);
console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);
index.html
index.html は views/tmpl/index.html におきます。
${title}
${title}
styles.less
styles.less は views/less/styles.less におきます。
.class {
width: 1 + 1px;
}
a {
text-decoration: underline;
&:hover {
text-decoration: none;
}
}
Trackbacks: 0
- Trackback URL for this entry
- Listed below are links to weblogs that reference
- Node.jsでLESSファイルを動的にコンパイルする from 5509
Leave a comment