vscodeでLaravel Mixのタスクを分ける

タグ: Laravel   vscode  

追記:変更点はGitHubのリポジトリーへまとめました。

注意:Laravel Mixで簡単に実現できる範囲での自動化で済み、安定してMixのwatchが動作する環境であれば、わざわざ複雑な方法を取る必要ありません。分割可のアイデアはMixのwatchが不安定な状況で役に立つ可能性がある方法です。ちなみに、私はMixを使っていません。CSSフレームワークにはFoundationが好みのため、Foundationが提供する自動化の仕組みを使っています。ただ、Foundation自体のメジャーアップデートが数年されておらず、そのため古いため差分コンパイルされないため、いくらか工夫が必要です。いろいろ手を入れたときの気づきをLaravel7の環境で使用する場合のサンプルとして書いています。

前記事で、vscodeをLaravel環境向けに設定する方法を説明しました。その中で、Laravel Mixのwatchとvscodeの保存時の実行を組み合わせる方法を説明しました。

Laravel Mixのwatchはnodeのパッケージを使いファイル監視を行います。vscodeも何を使っているかは調べていませんが、ファイル監視を行っています。リソースが足りないことはvscodeが知らせてくれますが、設定や環境により動作が不安定になり得ることを説明しました。

Laravel Mixの監視(watch)とvscodeを使っても、大抵の場合不具合が起きることは無いでしょう。もし、システムのハングやエラーが発生するなど明確な不具合でなくても、「たまに固まる」とか「たまにエラーが起きる」などでも、ファイル監視とそのイベントによりどんな影響をファイルシステムへ与えているのかなどを調査すると原因が見つかるかも知れません。可能であればファイル監視の結果、ファイルに影響を与えるシステムを複数使うのは、ユーザーレベルで避けたほうがベターです。

そこで、前記事ではLaravelプロジェクトのリソース下のjsファイルとscssファイルを保存するタイミングで、Laravel Mixをつかうnpmスクリプトを起動しリソースコンパイルをする方法と、watchスクリプトで最低限のリソースを監視し、BrowserSyncによりリソース変更時のブラウザの再描写自動化を説明しました。

可能であればvscodeからブラウザの再描写を行う方法を探しています。存在しているはずなのです。今の所vscodeエクステンションでもBrowserSyncを使うものがありますが、動作を確認できたものがなかったため、Laravel Mixが用意するBrowserSyncを使う方法がLaravelでは一番手っ取り早いと判断しました。

必要なvscodeエクステンションについては前記事をご覧ください。この記事ではLaravel Mixのタスクを複数に分ける方法と、vscodeに関連する設定を説明します。

Laravel Mixのタスクを分ける

Laravel Mixは設定ファイルでMixのインスタンスを作成し、そこでMixが行うタスクを記述する方法を取っています。これはMixが「手軽」にリソースコンパイルなどの作業を提供するのを目的としているからです。

手軽なのですが、実際に開発が進むにつれ単純な方法だけでなく、条件により行うタスクを調整したくなります。そうするとLaravel Mixのドキュメントを読み込んだり、それで足りなければwebpackのドキュメントを読み込んだりと調べることが増え、結局Mixを使わずにwebpackを直接使用する方もいるでしょう。

今回はLaravel Mixが提供する機能で満足しているが、条件により実行するMixのタスクを切り替えたいときのヒントです。MixのIssueのneeravpさんの回答とそれ以降のやりとりを具体的に説明します。

目標とするのは、コンパイルをjsとcssとで分割し、watch時はリソースのコンパイルを行わずに画面の再描写に専念させることです。

webpack.mix.js

もともと存在している設定ファイルを次のように書き換えます。

if (process.env.section) {
   require(`${__dirname}/webpack.mix.${process.env.section}.js`);
}

process.env.sectionにより、読み込む設定ファイルを決めるというアイデアです。

package.json

npmのタスクは、package.jsonファイルのスクリプトとして定義されています。scriptsの部分を以下のように変更してください。

    "scripts": {
        "dev": "npm run compile-js",
        "compile-js": "cross-env process.env.section=jscompile NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "compile-css": "cross-env process.env.section=csscompile NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch": "cross-env process.env.section=watch NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js --watch",
        "watch-poll": "npm run watch -- --watch-poll",
        "prod": "npm run production",
        "production": "cross-env process.env.section=production NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "vagrant": "vagrant up"
    },

"hot"と"development"のスクリプトは削除しています。"compile-css"と"compile-js"、"vagrant"を追加しています。"vagrant"スクリプトはフォルダーを開いたときに、Homestead Boxを自動実行させるために仕込んでいます。

それぞれ、process.env.sectionに異なった値を指定しています。

ちなみに、npm run watch-pollが正しく動作するかは確認できませんでした。

webpack.mix.production.js

let mix = require("laravel-mix");

mix.js('resources/js/app.js', 'public/js')
    .sass('resources/sass/app.scss', 'public/css');

npm run prodnpm run productionで実行されるスクリプトです。jsとcssのコンパイルを行う設定です。

webpack.mix.jscompile.js

let mix = require("laravel-mix");

mix.js('resources/js/app.js', 'public/js');

jsのコンパイルのみを行うスクリプトです。

webpack.mix.csscompile.js

let mix = require("laravel-mix");

mix.sass('resources/sass/app.scss', 'public/css');

Laravel Mixの場合はSASS前提でcsccファイルですが、cssのみをコンパイルする設定です。

webpack.mix.watch.js

let mix = require("laravel-mix");

mix.browserSync({
   ignore: [
      "resouces/views/mail/**/*.php",
      "resouces/views/mails/**/*.php"
   ],
   files: [
      "resources/views/**/*.php",
      "public/**/*.*"
   ],
   proxy: {
      target: "http://homestead.test/"
   }
});

npm run watchで、ビューファイルかpublic下のファイルに変更があれば、BrowserSyncでブラウザを再描写する設定です。

jsとcssのコンパイル結果はpublicディレクトリー下の適当な位置に出力されます。それ以外に画像ファイルの追加や変更時にも描写をリフレッシュすべきでしょう。そのため、publicディレクトリー下全部をファイルの監視対象にします。

もう一つのファイル監視対象は、ビューファイルです。phpファイルですのでリソースとは関係ありません。しかし、ビューを変更すればWebページの表示も変化するわけですから、ブラウザを再表示するのは理にかなっています。

Laravelの場合、HTMLメールもビューファイルで定義できます。メールビューの場所は指定されていませんが、通常ビューディレクトリー下のmailmailsディレクトリーの下になるでしょう。メールのビューに変更があっても、Webページの描写には関係が無いため、ignoreで無駄な再描写をしないように除外しています。

実際にはこの程度の数の監視であれば問題にはならないのがほとんどですが、可能な限り限定しておくのは無駄なリソースの消費を防ぎますし、デッドロックさせたり、無限ループさせるような設定を意図せず行うのを防ぐことにもつながります。

targetには開発環境に割り当てたURLかIPアドレスを指定します。http://homestead.test/は、Homestead環境をLaravelプロジェクトファイルにインストールした場合のデフォルトURLです。

.vscode/tasks.json

次にvscodeの設定ファイルを変更しましょう。まずはタスクです。(npmのスクリプトもタスクと呼ばれることが多いですので混同しないでください。vscodeがnpmなどの自動化タスクをvscode自身のタスクとしても自動的に認識するので、混同しがちです。)

{
    "version": "2.0.0",
    "tasks": [
        {
            "type": "npm",
            "script": "compile-js",
            "problemMatcher": []
        },
        {
            "type": "npm",
            "script": "compile-css",
            "problemMatcher": []
        },
        {
            "type": "npm",
            "script": "vagrant",
            "problemMatcher": [],
            "runOptions": {
                "runOn": "folderOpen"
            }
        }
    ]
}

jsファイルのコンパイル、cssファイルのコンパイル、vagrant起動の3タスクの指定です。

vagrant起動タスクには、起動オプションを指定しており、フォルダー(ディレクトリー)を開くのがきっかけで自動的に実行されます。これにより直接フォルダーを開く、前回最後に利用していたフォルダーがvscodeを立ち上げたときに自動的に開かれるといったタイミングで、vagrant upが実行されます。(追記:ワークスペースを利用している場合、エクステンションが動作しません。)

もちろん他のコマンドも実行可能です。HomesteadをLaravelプロジェクトディレクトリーに直接インストールする方法ではなく、複数プロジェクト共用のHomesteadを利用する場合は、たとえばcd ~/Homestead && vagrant upのように、ディレクトリーを移動した後に起動コマンドを実行する要指定できます。

.vscode/settings.json

フォルダー下に存在する.vscodeディレクトリーはvscodeが設定などを保存しておくディレクトリーです。その中のsetting.jsonファイルが、そのディレクトリーを開いたときに適用される設定を示しています。

前記事に、ファイルの保存タイミングでvscodeのタスクを実行するエクステンションを紹介しました。その設定を内容はダブりますが説明します。

以下の内容を追記します。

"triggerTaskOnSave.tasks": {
    "compile-js": [
        "resources/js/**/*.js"
    ],
    "compile-css": [
        "resources/sass/**/*.scss"
    ]
},

実行するタスク名と、保存時にそのタスクを実行するファイルをglob形式で指定します。

実行の流れ

今までの設定で、何が起きるのかを簡単に説明します。

フォルダーオープン時

  1. vscodeがフォルダーオープン時に起動する指定のあるタスクとして"vagrant"を見つける。
  2. "vagrant"タスクは、"npm"の"vagrant"タスク(スクリプト)であるとtasks.jsonに指定があるため、"npm run vagrant"が内部のターミナルで実行される。
  3. npmは"vagrant"スクリプトとして実行するコマンドとして、"vagrant up"を実行する。
  4. VagrantはVagrantfileに従いBoxを起動する。

これで、Homestead環境が動作します。

jsファイル変更時

  1. vscodeでファイルが保存される。
  2. 拡張機能(エクステンション)が保存されたファイルパス(resources/js/**/*.js)とマッチするか調べる。
  3. マッチしたら、該当のタスク(compile-js)をvscodeの設定から調べる。
  4. compile-jstasks.jsonで"nmp"の"compile-js"スクリプトであると指定されているので、nmp run compile-jsが実行される。
  5. Mixが実行され、webpack.mix.jsを設定ファイルとして取り込む。
  6. webpack.mix.jsでは、process.env.sectionjscompileであるため、webpack.mix.jscompile.jsが読み込まれる。
  7. resources/js/app.jsをコンパイルし、結果をpublic/jsへ書き出す。

cssも同様な動作のため省略します。

Laravel Mixによるページの再ロード

  1. npm run watchの実行で、webpack.mix.jswebpack.mix.watch.jsの順番に読み込まれる。指定されたファイルを監視する。
  2. 管理ファイルの変更イベントにより、BrowserSyncが接続しているデバイスのページを更新するように通信し、各ページでリロードが実行される。

フォルダーオープン時にvagrant upを自動実行するのと同様に、watchスクリプトを自動実行することも可能です。ただ、各自の環境によりBrowserSyncが初回に開くページは表示されない可能性があります。つまり、Homestead環境が立ち上がっていない場合に起動がかかりますが、BrowserSyncの監視が始まりブラウザを自動的に開くよりもマシンが立ち上がるのが通常時間がかかるわけです。ページにアクセスする時はHomestead環境≒サーバーがまだ立ち上がっていません。その場合、Webサーバーが立ち上がった時点で、自分で再接続する必要があります。

方法は、ここまでの内容を理解していれば簡単ですので、省略します。

ワークスペースでの使用

【重要】どうやらvscodeがタスクをタスクとして理解するのは、設定ファイルの読み込みではなく、一度実行する必要があるらしいため、ワークスペースへLaravelのプロジェクトフォルダーを取り込んだら、各タスクをメニューの「ターミナル」、「タスクの実行…」から一度走らせておく必要があるようです。

ワークスペースで使用する場合、フォルダーオープン時に起動するタスクの実行をvscodeが確認してきます。本来は一度確認すれば再度確認されない仕様のようなのですが、その後も何度か尋ねられたり、黙ったまま起動時のタスクを実行しないこともあるようです。

これは、vscodeを何度か停止・起動を繰り返しているうちに安定して実行されるようになります。推測ですがvscodeは経験上、ユーザーの使用状態の保存が上手くいかない場合があり、それが絡んでいるのではないかと思います。

同期プログラミングの宿命であり、たくさんの人がエクステンション開発に関わるオープンソースの宿命でもあります。再現したり、しなかったり、特定の環境で起きたりするものは、原因追求が困難ですし、時間を取ってわざわざ追求する人も少ないため、こうした不具合は残りがちです。