lessの2.0がリリースされました。
http://github.com/less/less.js/blob/master/CHANGELOG.md
世間的にはSass/SCSSが主流だと思いますが、動作にはRubyが必要です。
一方でlessは全部JSで記述されているため、(僕にとっては)使い勝手が良い。
JSPのように、初回のHTTPリクエストが来た時にJVMプロセス内でlessを動かしてコンパイルするサーブレットを動かしています。
less-2.0は内部実装が大幅に変わった
less-1.7(一つ前のリリース)までは、RhinoでEnvJSをロードしておけば*1、概ね問題なくlessを動かすことができていました。
これまではバージョンアップを重ねても大きな互換性問題は出なかったので、今回も何も考えずにアップデート。すると動かなくなる。どうやら何かが変わったようです。
ソースを見てみると、Promiseが必要になったっぽい。
こんな感じ。
var PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise, contexts = require("./contexts"), Parser = require('./parser/parser'), PluginManager = require('./plugin-manager');
もちろん互換性のために、Promiseがない環境では自前で定義するようにもなっています。
less-2.0.0.jsの9053行目はこんな感じ。
module.exports = Promise; function Promise(fn) { if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new') if (typeof fn !== 'function') throw new TypeError('not a function') ... }
しかしウチは運用上の理由によりグローバルスコープをロックしている*2ため、勝手にグローバル関数を宣言することはできません。
RhinoにもEnvJSにもPromiseはありませんので、グローバルスコープをロックする前に自前で定義する必要があります。
とりあえず動くようにする
今回はPromiseの実装として、使い慣れているjQuery.Deferredを使いました。
ついでに、EnvJSの仮想DOM上にscriptタグが一つ以上存在しなければless-2.0.0.jsの788行目でエラーになるので、無理やり追加しています。document.currentScriptがそれ。
(function() { window.Promise = window.Promise || function(func) { return $.Deferred(function(dfr) { func(function(x) { dfr.resolve(x); }, function(x) { dfr.reject(x); }); }).promise(); }; if(!document.currentScript) { document.currentScript = (function() { var scriptElem = document.createElement('script'); return scriptElem; }()); } }());
これで元通りに動くようになりました。