Skip to content

Laravelのblade formatterを作った

Posted on:2020-10-01 at 05:35 AM

作ったもの

何が出来るか

format例:

Input:

@extends('frontend.layouts.app')
@section('title') foo
@endsection
@section('content')
<section id="content">
<div class="container mod-users-pd-h">
    <div class="pf-user-header">
    <div></div>
    <p>@lang('users.index')</p>
    </div>
        <div class="pf-users-branch">
            <ul class="pf-users-branch__list">
                @foreach($users as $user)
        <li>
            <img src="{{ asset('img/frontend/icon/branch-arrow.svg') }}" alt="branch_arrow">
            {{ link_to_route("frontend.users.user.show",$users["name"],$users['_id']) }}
        </li>
        @endforeach
      </ul>
      <div class="pf-users-branch__btn">
      @can('create', App\Models\User::class)
            {!! link_to_route("frontend.users.user.create",__('users.create'),[1,2,3],['class' => 'btn']) !!}
            @endcan
        </div>
  </div>
    </div>
</section>
@endsection
@section('footer')
@stop

Output:

@extends('frontend.layouts.app')
@section('title') foo
@endsection
@section('content')
    <section id="content">
        <div class="container mod-users-pd-h">
            <div class="pf-user-header">
                <div></div>
                <p>@lang('users.index')</p>
            </div>
            <div class="pf-users-branch">
                <ul class="pf-users-branch__list">
                    @foreach ($users as $user)
                        <li>
                            <img src="{{ asset('img/frontend/icon/branch-arrow.svg') }}" alt="branch_arrow">
                            {{ link_to_route('frontend.users.user.show', $users['name'], $users['_id']) }}
                        </li>
                    @endforeach
                </ul>
                <div class="pf-users-branch__btn">
                    @can('create', App\Models\User::class)
                        {!! link_to_route('frontend.users.user.create', __('users.create'), [1, 2, 3], ['class' => 'btn']) !!}
                    @endcan
                </div>
            </div>
        </div>
    </section>
@endsection
@section('footer')
@stop

ディレクティブレベルでネストされ、PSR-2準拠、HTMLもネストされていることが分かる

Motivation

いい加減人類はコードの手動フォーマットという苦痛から解放されるべきで、もはやランタイムレベルでフォーマッタを入れるのは常識(言葉が強い)だけどLaravel bladeにはまだまともなformatterがなかったので作った

ここでまともと言っているのは少なくとも

ということだけどこれを実現しているformatterがなかった

なぜなかったのか

formatterが出来るまで

指針

まずライブラリを作るに当たって大まかな指針を決める

指針が現実的に想像出来るレベルになったら実装に入る. とりあえず手を動かして指針に戻る場合もある

今回は既にLaravel Blade Snippetsが簡易的にFormatterを実装していたのでそれをベースにこのライブラリが出来ていない箇所をブラッシュアップしていく形になった

Laravel Blade Snippetsはjs-beautifyでHTMLをFormatしているだけであくまでbladeディレクティブもHTMLのタグのようなものとして扱っていたためbladeディレクティブのネストやPSR2への準拠といったところが実現出来ていなかった

またCLIでの実行をサポートしておらずあくまでVSCodeのプラグインとしてのみ動作していたためCIでのprogramiticalなformat検査などが実行出来なかった

blade-formatterではそういった足りないところを補う形で当初は作ることにした
実際作った後から見るとほぼ別物になっていたので最初の指針で残っているのは大まかにlexerを作るか作らないかという違いくらいだった気がする

実装

まずどういったソフトウェアでもそうだけど最小限のPoCを実装する

PoCで十分最終的に実現したいソリューションを達成出来る手応えを得られた時点でリソースと相談して最後まで作るかどうか決める

ただあくまで個人的に作るOSSはそれが僕には楽しかったから作るという場合が往々にしてあるのでリソースといったところはあまり気にしない.

今回はまず要件としてbladeディレクティブをネストしたいのがあり、これを実現するためにはbladeファイルをパースしtokenizeした上でblade特有のディレクティブをtokenとして認識する必用があるのだけど、ここはTextMateのtmBundle形式の言語ファイルを使用した

VSCodeには実はvscode-textmateというtextmateのbundleを処理するためのライブラリがbuiltinで入っており、vscode-textmate経由で各種言語のSyntax Highlightやtokenizeを行っている

そのためtmBundle形式のファイルさえ用意すれば比較的用意にSyntax Hilighterなどは作れるのだがこれを利用し、vscoode-textmate自体はVSCodeが存在しなくても動くのでbladeファイルのtokenizerとして利用した

結果としてはこの選択が後で作るVSCode向けの拡張のvscode-blade-formatterを作る時にVSCodeビルトインのvscode-textmateを直に利用出来る結果になったため互換性が増してよかった

反省点

参考にした