読者です 読者をやめる 読者になる 読者になる

メモを揉め

お勉強の覚書。

Stylus JavaScript API とプラグインの仕組みについて

f:id:alluser:20161030234249p:plain

先日 foovar という Stylus のライブラリを公開しました。
Stylus の変数を JS ファイルにエクスポートするためのライブラリです。

memowomome.hatenablog.com

その時に調べた Stylus JavaScript API の詳細や、プラグイン作成の方法です。

仕様の変更などにより記事の内容が古くなったらすぐ分かるように、記事のテストを書いてみました。

CircleCI

Stylus プラグインの仕組み

プラグインの実体は関数を返す関数(高階関数)です。
これを module.exports するだけでプラグインの体を成します(役に立たないということを除けば)。
npm で公開すればすぐにプラグインとして利用可能です。

module.exports = function() {
  return function() {};
};

外側の関数は use (後述)した際に呼ばれ、オプションを引数に受取ります。
内側の関数は Rendererインスタンスを引数に受け取ります(コード中stylus)。

module.exports = function(...options) {
  return function(stylus) {};
};

内側の関数内の this はこの Renderer インスタンスに束縛されて呼び出されます。
require('stylus')() で得られるインスタンスも同じく Rendererインスタンスです。

module.exports = function(...options) {
  return function(stylus) {
    this.condtructor.name // 'Renderer'
    this === stylus; // true
    this.constructor === require('stylus')().constructor; // true
  };
};

基本的にはこの Renderer インスタンスを介して Stylus の世界とやり取りをすることになります。
Stylus から呼び出せる関数を JS で定義したり、あらかじめ用意した .styl ファイルへのパスを通したりといった感じです。
この Renderer インスタンスAPI については公式のドキュメントに詳しい情報があります。

プラグインの読み込みとオプションの渡し方

プラグインを読み込む方法は3つあります。

  • ビルトイン関数 use(path) を使う
  • JavaScript API.use(fn) を使う
  • CLI--use name オプションを使う

いずれも use という言葉が使われており、このキーワードがプラグイン読み込みを指すと思われる。

ビルトイン関数 use(path)

JS ファイルのパスを指定して呼び出す。
プラグイン名での呼び出しは出来ない。

第2引数にはハッシュをオプションとして渡せる。 渡せるのはハッシュのみで、それ以外はエラーになる。
また第3引数以降は無視される。

use('./my-plugin.js')
use('../../node_modules/plugin/index.js') // 名前での呼び出しは出来ない。 node_modules 配下のエントリポイントを直接指定することは出来る
use('../../node_modules/plugin/index.js', { foo: 20px }) // オプションに渡せるのはハッシュのみ

JavaScript API.use(fn)

前述の高階関数内側の関数のみを渡す。
require で読み込んだモジュールを実行して内側の関数を取り出し use に渡すという使い方になる。

const stylus = require('stylus');

stylus(source)
  .use(require('plugin-name')())
  .render(/*...*/);

外側の関数にはオプションを渡すことが出来る。
require で読み込んで関数を直接呼び出すだけなので、好きな数の引数を好きな型で渡すことができる。
use(path)CLI--use オプションとAPIを揃えるために、オプションはオブジェクト1個に限定したほうがいいかもしれない。

stylus(source)
  .use(require('plugin-name')({foo, 'bar'}))
  .render(/*...*/);

CLI--use name オプション

--use に続けてプラグイン名を渡すと読み込んでくれる。
短縮形は -u

$ stylus --use plugin-name path/to/source.styl
# あるいは
$ stylus -u plugin-name path/to/source.styl

--with に続けて文字列を渡すと、 JS としてパースされ外側の関数にオプションとして渡される。
なぜかドキュメントに載ってないし、 --help にも出てこない。

この場合複数の引数は指定できないので、複数のオプションを扱いたい場合はオブジェクトや配列で渡す必要がある。

$ stylus -u plugin-name --with '{ foo: true }' path/to/source.styl

Stylus のトークンを表現するクラス

ここで記載する API はごく一部です。
より詳しいクラスの詳細はここらへんを見ると良さそう。

これらのコンストラクタを new して Stylus のトークンを自前で生成することもできます。

トークンクラスへのアクセス

evaluator.renderer.nodes.* で Stylus のトークンクラスにアクセスできる。
evaluatorJavaScript APIdefine(name, fn) で定義した関数内から this で参照することができる。

const fn = function() {
  return new this.renderer.nodes.String('hello world');
};

module.exports = function() {
  return function(renderer) {
    renderer
      .define('hello-world', fn);
  };
};

以下、各クラスのコンストラクタの使い方、プロパティの型、コンソールに出力した結果です。

Expression

式を表すクラス。
nodes 内の要素が複数であれば tuple もしくは list となる。(要素が一つの場合を長さが1の tuple と言うこともできる)
tuplelist の違いは isList で判定できる。

constructor

  • Expression(isList)
    • isList: boolean

instanceMethod

  • push(node): nodethis.nodes に追加する
    • node: node

instanceProperty

  • nodes: array
  • isList: boolean

{
  lineno: 1,
  column: 14,
  filename: 'index.css',
  nodes: [
    {
      lineno: 1,
      column: 14,
      filename: 'index.css',
      val: 'some string',
      string: 'some string',
      prefixed: false,
      quote: '\''
    }
  ],
  isList: undefined
}

Object

Stylus のハッシュを表すクラス。
vals には JS のオブジェクトが入っており、各キーにはさらに Stylus のトークンが入る。

constructor

  • Object()

instanceMethod

  • set(key, value): value の値を key にセットする
    • key: string
    • value: node

instanceProperty

  • vals: object

{
  lineno: 1,
  column: 10,
  filename: 'index.css',
  vals: {
    foo: {
      lineno: 1,
      column: 19,
      filename: 'index.css',
      nodes: [Object],
      isList: undefined
    }
  }
}

String

Stylus の文字列を表すクラス。

constructor

  • String(val, quote)
    • val: string

instanceProperty

  • string: string
  • val: string

{
  lineno: 1,
  column: 14,
  filename: 'index.css',
  val: 'some string',
  string: 'some string',
  prefixed: false,
  quote: '\''
}

Unit

単位付き(あるいは無し)の数値を表すクラス。
10px1.7em200ms0 などは全て Unitインスタンスとなる。

constructor

  • Unit(val, type)
    • val: number
    • type: string

instanceProperty

  • val: number
  • type: string
  • raw: string

{
  lineno: 1,
  column: 10,
  filename: 'index.css',
  val: 20,
  type: 'px',
  raw: '20px'
}

RGBA

RGBA形式の色を表現するクラス。
#112233 , #11223344 , rgb(11,22,33) , rgba(11,22,33,44) などは全て RGBAインスタンスとなる。
16進数表記の時のみ raw プロパティが存在し、16進数表記の文字列が取得できる。

constructor

  • RGBA(r, g, b, a)
    • r: number
    • g: number
    • b: number
    • a: number

instanceProperty

  • r: number
  • g: number
  • b: number
  • a: number
  • raw: string

{
  lineno: 2,
  column: 16,
  filename: 'index.css',
  r: 17,
  g: 34,
  b: 51,
  a: 0.26666666666666666,
  name: '',
  rgba: [Circular],
  raw: '#11223344'
}

HSLA

HSLA形式の色を表現するクラス。
hsl(11,22,33) , hsla(11,22,33,44) などは全て HSLAインスタンスとなる。

constructor

  • HSLA(h, s, l, a)
    • h: number
    • s: number
    • l: number
    • a: number

instanceProperty

  • h: number
  • s: number
  • l: number
  • a: number

{
  lineno: 3,
  column: 12,
  filename: 'index.css',
  h: 11,
  s: 22,
  l: 33,
  a: 0.4,
  hsla: [Circular]
}

Ident

autoinheritnone 等のトークンを表現するクラス。

constructor

  • Ident(name, val, mixin)
    • name: string

instanceProperty

  • name: string
  • string: string

{
  lineno: 1,
  column: 13,
  filename: 'index.css',
  name: 'auto',
  string: 'auto',
  val: null,
  mixin: false
}

Call

cubic-bezier() 等の関数呼び出しを表現するクラス。

constructor

  • Call(name, args)
    • name: string
    • args: expression

instanceProperty

  • name: string
  • args: expression

{
  lineno: 1,
  column: 40,
  filename: 'index.css',
  name: 'cubic-bezier',
  args: {
    lineno: 1,
    column: 20,
    filename: 'index.css',
    nodes: [ [Object], [Object], [Object], [Object] ],
    isList: undefined,
    map: {}
  }
}

テスト

記事の公開時点では Stylus v0.54.5 を対象にテストしています。
all-user/inspect-stylus