動機
だいたいの問題は上記が解決してくれるとして、それ以外のよく使う方言をstylusプラグインとして公開したい場合があると思うので、自分が実践した手順を紹介します。
環境
node-v6.2.1
および 下記package.json
を使用します。
mkdir foo
cd foo
vim package.json
npm install
{
"scripts": {
"build": "stylus index.styl --out index.css",
"test": "ava",
"lint": "xo"
},
"devDependencies": {
"ava": "^0.15.2",
"stylus": "^0.54.5",
"xo": "^0.15.1"
},
"xo": {
"space": 2
}
}
stylus
に認識させる
stylusは@import
を使用したさいに、登録してあるpaths
から順にファイルを精査します。
CLIから--include
オプションを使用せずコンパイルした場合、現在のディレクトリを基準にするため、以下は正常に動作します。
tree -L 1
# .
# ├── index.styl
# └── my-plugin.styl
npm run build
# h1 {
# font-size: 10vw;
# }
@import 'my-plugin'
h1
font-size 10vw
JavaScriptから実行した場合も同様です。
const fs = require('fs');
const stylus = require('stylus');
const entry = fs.readFileSync('index.styl', 'utf8');
stylus(entry)
.render((err, css) => {
console.log(css);
// h1 {
// font-size: 10vw;
// }
});
node_modules
として動作させる
既存のプラグインはnpm install
でnode_modules
にインストールできますが、使用する前に--use my-plugin
なり、.use(myPlugin())
などで登録を行います。
いずれにしても、プラグインをrequire
し、require
したディレクトリ直下のindex.js
か、同じくディレクトリ直下のpackage.json>main
に定義されている位置のファイルを読み込み、module.exports
で定義されている関数が実行されるはずです。
module.exports = function () {
return function (stylus) {
stylus.include(__dirname);
};
};
nibの実装では、実行した関数が関数を返し、その中で初期化処理を行っています。
注目すべきは最初のstyle.include(__dirname)
という箇所で、これはstylusのpaths
にそのディレクトリ(node_modules/nib/lib
)を登録しています。ですので、この状態で前章のindex.styl
に@import 'nib'
と書くと、node_modules/nib/lib
のnib/index.styl
またはnib.styl
が読み込まれます。
仮に、先に登録した
paths
に同名のフォルダやファイルが存在していた場合、そちらが優先されるようです。
この動作は、実際にnode_modules
内にフォルダを作成してみて、CLIの動作を確認してみるとイメージしやすいです。
mkdir node_modules/my-plugin2
vim node_modules/my-plugin2/index.js
vim node_modules/my-plugin2/my-plugin2.styl
module.exports = function () {
return function (stylus) {
stylus.include(__dirname);
};
};
h2
font-size 5vw
CLIで--use my-plugin2
または--include node_modules/my-plugin2/
で、my-plugin2
をpaths
に追加すると、コンパイルに成功することを確認できます。
@import 'my-plugin'
@import 'my-plugin2'
npm run build
# failed to locate @import file my-plugin2.styl
npm run build -- --use my-plugin2
npm run build -- --include node_modules/my-plugin2/
# h1 {
# font-size: 10vw;
# }
# h2 {
# font-size: 5vw;
# }
テスト環境の整備
描写結果を確認したい場合は karma を使い、ブラウザが計算したスタイルを検証するべきでしょうが、この記事では言及しません。
今回は、コンパイルしたstyl
が指定のcss
に一致するかのテストをava + xoで行います。
xo@0.15
はbabel-eslint@6を含んだlinter
です。生eslintに比べて、プロジェクト自体のdevDeps
を減らせるので、プロジェクトを小さくしたい場合にお勧めです。
これまでの章で作成したindex.js
, my-plugin.styl
以外のファイルは以降使用しないので、削除してください。
tree -L 1
# .
# ├── index.js
# ├── my-plugin.styl
# ├── node_modules
# ├── package.json
# └── test.js
./my-plugin.styl
をmixin
に変更します。
large-font()
font-size 10vw
npm test
で使用するテスト本体を記述します。
import test from 'ava';
import stylus from 'stylus';
import myPlugin from './';
const render = (str) => {
let css;
stylus(str)
.use(myPlugin())
.import('my-plugin')
.set('compress', true)
.render((err, data) => {
if (err) {
throw err;
}
css = data;
});
return css;
};
const specs = [
{
description: 'h1のfont-sizeを10vwにすべき',
code: `
h1
large-font()
`,
expected: 'h1{font-size:10vw}'
}
];
specs.forEach(spec => {
test(spec.description, t => {
const {code, expected} = spec;
t.true(render(code) === expected);
});
});
compress
オプションを利用することでテストしやすいcss
が生成できるので、stylusの初期化処理を含めてヘルパ関数として定義します。test
内ではヘルパの実行と、結果の比較のみを行い、テストデータspecs
を増やしていくことで、検証内容を自動で増やすことが可能です。
npm test
でテストが通ること、テスト内容を変更して失敗することを確認します。
npm test
# 1 passed
vim test.js
npm test
# 1 failed
実施例
59naga/stylus-responsive-breakpoints: a media queries of stylus block mixins
angular-materialのメディアクエリのテンプレートをstylプラグイン
として移植したものです。他にiphone
, ipad
をdevice-width
で検出するメディアクエリなどを追加しています。