◆ AppData\Roaming に preact-cli フォルダを自分で作らないとダメ
◆ 証明書作成用の関数がおかしいので修正必要

preact は htm と合わせた standalone で使うくらいで webpack とか事前ビルドは全然使ってなかったのですが preact-cli というのをみかけたので使ってみました
https://github.com/preactjs/preact-cli

バージョンは現最新版の 2.2.1 です

create

preact create を使えば用意されてるテンプレートを使ってすぐに使える状態にしてくれます

preact create default test1

だと default がテンプレートで test1 が作るフォルダ名です

SSR に対応していたり ServiceWorker ファイルまで作ってくれたり思った以上に高機能です
ただ個人的には内部でよくわからないことを色々されてるのがあんまり好きじゃないので standalone 版で使うだけでいいかなって気はしました

serve できない

とりあえずいくつかのテンプレートの初期状態を見てみようとしたのですが……
default と simple は問題なく動いたのですが material テンプレートだけは serve ができません

こんなエラーが起きます

Setting up SSL certificate (may require sudo)...
Attempting to spawn simplehttp2server to generate cert.
Failed to generate dev SSL certificate: Error: spawn C:\Users\winuser\Desktop\33\mate\node_modules\simplehttp2server\vendor\simplehttp2server_windows_amd64.exe ENOENT
Falling back to shared directory + simplehttp2server.
(dir: C:\Users\winuser\AppData\Roaming\preact-cli)

server error> Error: spawn C:\Users\winuser\Desktop\33\mate\node_modules\simplehttp2server\vendor\simplehttp2server_windows_amd64.exe ENOENT
Error: spawn C:\Users\winuser\Desktop\33\mate\node_modules\simplehttp2server\vendor\simplehttp2server_windows_amd64.exe ENOENT

他では動いたのにと思って serve コマンドを見てみると default と simple では sirv を使っていました
どちらも serve コマンドの中身はこれです

preact build && sirv build --cors --single

https://github.com/preactjs-templates/default/blob/master/template/package.json
https://github.com/preactjs-templates/simple/blob/master/template/package.json

それに対して material の場合はこれです

preact build && preact serve

https://github.com/preactjs-templates/material/blob/master/template/package.json

普通に preact-cli の serve を使ってます
preact serve 自体がダメのようですね

対処する

フォルダが見つからない

まずは原因を調べようと思います

最後に出てるエラーは ENOENT です
これはファイルやフォルダがみつからないってエラーです

preact-cli の serve では http2 サーバを作って serve するようで そのために simplehttp2server というツールを使っています

create のときの自動インストールでエラーでもあったのかなと見てみるとエラーが出ているパスにはちゃんと exe が存在しました
よくわからないのでとりあえず直接その exe を実行してみるとサーバが起動して普通にブラウザからアクセスして使えました
build だけして serve は別に手動でやってもよさそうです

ただ ちゃんと解決したいなとも思うので 続けて調べてみました

この exe を実行している箇所で引数を表示してみると cwd に 「ユーザフォルダ\AppData\Roaming\preact-cli」 を指定していました
しかし Roaming 以下に preact-cli フォルダはないのでこっちが見つからずエラーです
エラーを見ると実行するファイルのほうが無いように見えますが原因は cwd の方でした
わかりづらいですね

原因は preact-cli が自分で使うフォルダなのにフォルダを作ってないことのようです
手動でこのフォルダを作ったらそこのエラーはなくなりました

……が また別の問題が起きました

終わらない

実行したら途中で止まります

Setting up SSL certificate (may require sudo)...
Attempting to spawn simplehttp2server to generate cert.

このメッセージが出た状態でそこから進みません
もうサーバの起動が完了してるのかなとデフォルトポートの 8080 にアクセスしてもつながりませんでした
これが正常というわけではないようです

プロセスを見てみると simplehttp2server 自体は起動していました
ただ 「-listen 40210」 というオプションがついていて 8080 ではないです

どこから 40210 が出てきたんだろうと ソースを見ていると証明書作成処理で止まってるようです
https://github.com/preactjs/preact-cli/blob/v2.2.1/src/lib/ssl-cert.js#L28

さっきはなかったのにと思ったら 同じく cwd がなくてエラーなのでスキップしてたようです
よく見るとログに「Failed to generate dev SSL certificate:」というのもありました

わざわざ別の場所に証明書作らなくても直接 simplehttp2server を起動したら動いたので不要な処理に見えます
消してしまおうかとも思ったのですがせっかくあるのでこのまま動かそうと修正してみます


spawnServerForCert 関数を見ると色々実装がおかしいです

まず execFile のコールバック関数は実行が終わってから呼び出されるのに 実行中に呼び出される前提のようなコードです

simplehttp2server のプログラム自体は自動で終了しないので 標準出力から 「listening」 を受け取ると証明書作成は完了したとみなしてプロセスを kill しています
またタイムアウトのチェックのタイマーを開始したりしています

パット見だと良さそうですが これらの処理が実行されるのは simplehttp2server の処理が終わってからです
kill しないと終わらないのですから これらが実行されることはありません

書いた人が spawn などの関数と間違って使ってそうです
これを spawn に置き換えてコールバック関数ではなく返り値を使って同様の処理にすれば動きます

ただ 「listening」 を含むかのチェックに正規表現を使ってますが RegExp に match メソッドは存在しません
正しくは test です
ここも修正必要です

実際に実行されてるのはバンドル済みなのでこういう感じに修正しました

const spawnServerForCert = () => new Promise((resolve, reject) => {
let cwd = (0, _persistPath2.default)('preact-cli');
let child = (0, _child_process.spawn)(_simplehttp2server2.default, ['-listen', ':40210'], {
cwd,
encoding: 'utf8'
});

let check = (() => {
var _ref2 = _asyncToGenerator(function* (chunk) {
if (/listening/gi.test(chunk)) {
clearTimeout(timer);
child.kill();
resolve({
key: yield _fs2.default.readFile(_path2.default.resolve(cwd, 'key.pem'), 'utf-8'),
cert: yield _fs2.default.readFile(_path2.default.resolve(cwd, 'cert.pem'), 'utf-8'),
keyPath: _path2.default.resolve(cwd, 'key.pem'),
certPath: _path2.default.resolve(cwd, 'cert.pem')
});
}
});

return function check(_x) {
return _ref2.apply(this, arguments);
};
})();

child.on("error", err => {
reject(err);
});

let timer = setTimeout(() => {
reject('Error: certificate generation timed out.');
}, 5000);

child.stdout.on('data', check);
child.stderr.on('data', check);
});

ここを直して再実行したら

Setting up SSL certificate (may require sudo)...
Attempting to spawn simplehttp2server to generate cert.
Listening on https://localhost:8080...

正常ぽいメッセージです
「https://localhost:8080/」にアクセスしたらページが見れました

preact 関係とかそこそこ大きな規模のプロジェクトですし リリース前に厳し目のレビューやテストがありそうなイメージでしたがそうでもなさそうですね

3.0

開発中の最新版だと直ってるのかなと思ってみてみると 3.0 ではそもそも serve コマンドがなくなっていました
それに合わせて ssl-cert.js ファイルもなくなってます

問題も多いし sirv とか別のツールつかえばいいじゃんとなったのでしょうか
serve 機能自体 デバッグ用ですし わざわざ作るほどじゃないと判断したのかもです

他ツール

ついでに他のツールで serve はどうやってるのか見てみると
react の react-scripts と vue の vue-cli では webpack-dev-server でした

無難に webpack 任せでいいのかもしれませんね