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