【laravel】laravel mixのwebpack.mix.jsの書き換えを省略したい!

4 min read読了の目安(約3600字

初書:2021/1/13
laravel : v8.21.0
php : 7.4.14

接続用リンク:素のPHPしか触ってなかった人がlaravelを触ってみる - Qiita

before -> 【laravel】laravel mixを使ってみる

前書き

前回、laravel mixを一通り(?)触ってみた。それで思ったことが一つ。

「ファイルを生成する度にwebpack.min.jsを書き換えないといけないんですか?」

答えはおそらくyesであり、軽く調べても出てこないということはおそらくそういうことだろう。
もしかしたらこの時点でもしかしたら使い方を間違えているかもしれない、でも間違いに気づくには誰かに聞くしかない
・・・という事で、webpack.min.jsのファイル追加を自動化します。

ファイル一覧を取得する

方法は簡単。フォルダ内のファイルを取得し、順番にmixに投げていけばいい。
という事でまずはファイル一覧を取得してみる。

webpack.min.jsの実行環境はnodeなので、nodeで検索。

Node.jsで高速にファイル一覧を取得するfs.readdirのwithFileTypesオプション - Qiita

fsを使用すれば良さそう?
という事で、今回は「fs.readdirのwithFileTypesオプションを使った判定方法」に記述してあるshowFiles関数をそのままお借りします。いや、多少不便な所があるのでそこだけ書き換えます。

webpack.min.js
const showFiles = (dirpath, callback, adddirpath = "") => {
    const dirents = fs.readdirSync(dirpath, { withFileTypes: true });

    for (const dirent of dirents) {
        const fp = path.join(dirpath, dirent.name);
        if (dirent.isDirectory()) {
            showFiles(fp, callback, adddirpath + "/" + dirent.name);
        } else {
            callback(fp, adddirpath);
        }
    }
};

引数adddirpathの追加と同期処理化です。
adddirpathは元々の階層からの相対位置を保持しておきます。
同期は後ほど使うmix関数が非同期だと上手くいかないからです。

mixに投げていく

単純に投げるだけではエラーが出るので(mixは一度しか呼べなさそう)、
状態を変数で持っておき、順番に呼び出していく方法をとる事にする。

webpack.min.js
let mixvar = mix;
dirlist.forEach((e) => {
    showFiles("resources/" + e.type, (filename,adddirpath) => {
        mixvar = mixvar[e.type](filename, "public/" + e.dir + adddirpath);
    });
});

少し面倒なことをしているので、順番に説明。
まずshowFilesの第一引数は、例えばcssならresources/cssとなるような形の値。eは後ほど書きます。
第二引数はファイル名がコールバック関数です。

コールバック関数内では、先ほど用意したmixverの関数を呼び出します。が、拡張子によって分けないといけないので、単純にmix.cssmix.jsのような書き方はできない。そのため、配列型式でmix[css]mix[js]という形で呼び出します。これでも呼び出せるのがjsのいい所(だと思う)。
そして引数には普通に元々のファイル名と出力先ディレクトリです。showFilesの時にadddirpathを追加したのは、例えばresources/css/auth/auth.cssの出力先がpublic/css/auth/auth.cssという感じで、階層を同じにするためにメモしていました。


とこんな感じで軽く説明しましたが、肝心のdirlist(e)の説明です。
設定の例はこんな感じ。

webpack.min.js
const dirlist = [
    { type: "css", dir: "css" },
    { type: "less", dir: "css" },
    { type: "js", dir: "js" },
    { type: "ts", dir: "js" },
];

dirlist自体は配列で、1要素はtypedirの要素をもったオブジェクトです。
typeresources内のフォルダ名兼拡張子です。なので、拡張子とフォルダ名が一致していない場合は書き換えが必要です。
dirpublic内のフォルダ名です。出力先。

このオブジェクトを、自身が使っている言語の数だけ増やしたり減らしたりします。

完成

出来上がったコードがこちら。

webpack.min.js
const mix = require("laravel-mix");
const fs = require("fs");
const path = require("path");
const dirlist = [
    { type: "css", dir: "css" },
    { type: "less", dir: "css" },
    { type: "js", dir: "js" },
    { type: "ts", dir: "js" },
];
const showFiles = (dirpath, callback, adddirpath = "") => {
    const dirents = fs.readdirSync(dirpath, { withFileTypes: true });
    for (const dirent of dirents) {
        const fp = path.join(dirpath, dirent.name);
        if (dirent.isDirectory()) {
            showFiles(fp, callback, adddirpath + "/" + dirent.name);
        } else {
            callback(fp, adddirpath);
        }
    }
};
let mixvar = mix;
dirlist.forEach((e) => {
    showFiles("resources/" + e.type, (filename,adddirpath) => {
        mixvar = mixvar[e.type](filename, "public/" + e.dir + adddirpath);
    });
});
mixvar.version(); // 任意

これで、ファイル(言語を増やす場合は別)を増やしても特に書き換えする必要なくコンパイルしてくれます。

終わりに

ところでlaravel mixって一度実行すると全てのファイルを再コンパイルする?そんなことはない?
もう少し色々と探ってみますか、、

その他参考サイト

特定ディレクトリ配下のファイルのみ(またはディレクトリのみ)を取得(Node.js v10.10以降版) - Qiita