フューチャー技術ブログ

Parcel 2.0 beta.1を試す

以前、TypeScriptのバンドラーとしてParcelの紹介をしました。

その後、6月ぐらいに2.0のbeta.1がリリースされていましたので試してみました。ウェブサイトも公開されています。

https://v2.parceljs.org/

パッケージ名も変わり、今までparcel-bundlerだったのが、parcelになります。今はベータなので、次のコマンドでインストールする必要があります。正式リリース後は@nextは不要になるはず。

$ npm install parcel@next

Parcel 2.0の新機能

ブログにまとまっています。TreeShakingの性能があがったよ、とかsource mapの生成がめちゃくちゃ早くなったよ、とあります。

https://medium.com/@devongovett/parcel-2-beta-1-improved-stability-tree-shaking-source-map-performance-and-more-78179779e8b7

それ以外に、.parcelrcというファイルで設定できるようになりました。なくてもデフォルトで以前と同じように動作しますが、これを使ってプラグインを細かくカスタマイズできるようになります。以前もプラグインがありましたが、プラグインでデフォルトの動作を変更できるようになります。

TypeScript機能の強化

TypeScriptの開発でいうと、今までは@babel/preset-typescriptで型情報を切り落として出力するだけだったので、確かにビルドはできるだけど型チェックができませんでした。.parcelrcで設定することで、本家のtscを使うことでtsconfig.jsonで設定が必要な追加機能をすばやく導入したり、型チェックを有効化できるようになります。

tscを使うには次の設定を使います。

.parcelrc
{
"extends": "@parcel/config-default",
"transformers": {
"*.{ts,tsx}": ["@parcel/transformer-typescript-tsc"]
}
}
.parcelrc
{
"extends": "@parcel/config-default",
"validators": {
"*.{ts,tsx}": ["@parcel/validator-typescript"]
}
}

後者の場合、tsconfig.jsonにも変更が必要な模様。

tsconfig.json
{
"include": ["src/**/*"]
}

型チェックを試してみました。これの実行時には@parcel/validator-typescriptのインストールが必要ですが、latestでデフォルトでインストールされるalpha-3はエラーになるので、最新のnightlyを入れます。

$ npm install @parcel/validator-typescript@2.0.0-nightly.430

やった!これが見たかった!

API Proxy機能の導入

あとは、APIサーバーのプロキシも設定ファイルできるようになります。ファーストパーティクッキーを使ったり、corsなしでウェブにアクセスするために同じオリジンで動作させたいということがあります。

本番環境はCDNのリダレクト機能とかでAPIサーバーと静的HTMLを同じオリジンで提供されているように見せることができても、開発環境ではフロントエンドのビルドサーバーとAPIサーバーを別のポートで動かしているという人も多いのではないでしょうか。そのためだけにAPIサーバーにローカル開発用のモードを作るのも面倒ですし、12 FACTORS APPに反するでしょう。

APIサーバープロキシ機能を使って、Parcelの開発サーバーから特定のパス以下をローカルのAPIサーバーにフォワードすると便利です。

試しに自分のオリジンの/api以下に向けてリクエストを飛ばすようなコードを書いてみます。リクエスト先は今Parcelの開発サーバーが稼働しているlocalhost:1234です。

tsx
import React, { useEffect } from 'react';

export function App() {
useEffect(() => {
(async () => {
const res = await fetch('/api/health');
if (res.ok) {
console.info(await res.json());
}
})();
}, []);
return <div>test</div>;
}

サーバーはlocalhost:3000で稼働させます。go run main.goで起動します。

main.go
package main

import (
"net/http"

"github.com/go-chi/chi/v4"
"github.com/go-chi/chi/v4/middleware"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)

func main() {
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Get("/api/health", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("ok"))
})
zerolog.SetGlobalLevel(zerolog.InfoLevel)
log.Info().Int("port", 3000).Msg("start watching")
http.ListenAndServe(":3000", r)
}

最後にproxy設定を書きます。パスのリライトなどもできますが、いろいろ面倒なので個人的にはフロントから見えているパスとサーバーのパスは一致させるようにしてリライトしないようにしています。

.proxyrc
{
"/api": {
"target": "http://localhost:3000/",
}
}

起動してみると、きちんと:1234へのリクエストが:3000にプロキシされていることがわかります。

今まではParcelのビルド機能とProxyの両方が入ったウェブサーバーを実装するしか手がなかったのと比べると、とても簡単になりました。

トラブルの回避

さて、以上の機能は問題なく動作していますが、まだまだベータですし、いろいろトラブルもありました。

問題があったらまずは検索しよう

かなり活発に開発されていてユーザー数も多いので、issueにいろいろ報告されていたり、回答されていたりもします。エラーメッセージで雑にリポジトリを検索するだけでもだいたい回避できるんじゃないかと思います。

https://github.com/parcel-bundler/parcel

Parcel 1は削除しよう

同名のシリアライザが登録されていますよ、的なエラーメッセージが出ました。Parcel 1のパッケージのparcel-bundlerを削除して、node_moduelsと.cacheを消してインストールしなおしました。

Error: Name already registered with serializer

TypeScriptのビルドでエラーが出る

上記でもすでに書きましたが、2.0.0-nightly.430を入れたら直りました。コード上は修正済みでも、まだリリースされていなくて、バージョン指定で入れ直さないと解決しないことがたまにあります。エラー報告の前に、最新のパッケージで試してみましょう。

アプリケーションの場合はpackage.jsonのmainは削除しよう

Parcel2は、package.jsonの外部向けの情報を元に、ビルドの出力先を変更します。package.jsonのmainがあればnpm installしたときに読み込めるように、出力先をそのパスに自動設定しますし、typesがあればTypeScriptの型情報もそこに出力します。気が利いたやつです。

しかし、npm init -yでデフォルトで生成するpackage.jsonには"main": "index.js"という項目があります。アプリケーションの場合、エントリーポイントはHTMLファイルにしているでしょうから、これと拡張子が合わない、というエラーが発生します。この項目を削除しておきましょう。

https://github.com/parcel-bundler/parcel/issues/3500

まとめ

現時点で、多少の問題はあるかもしれませんが、十分に使えるかな、と思っています。少なくとも趣味コード開発には十分だし、1.0で不満だった型エラーがチェックできない、API Proxyを立てるのが不便という2大ポイントが解決されるのが個人的にはとてもありがたいです。

使ってみると多少不便なところとか問題が発生するかもしれませんし、使ってフィードバックすると良いのではないかと思います。僕もさっそくドキュメントの修正のPRを出しました。その際も、なるべく手元でできることはして、すでに報告されているのと同じものを報告して開発者の手を煩わせないように、ここで軽く紹介したようなトラブルシュートをしてみると良いかと思います。