【Vite・Astro】ビルド時の日本語フォントのUnicodeエスケープシーケンス変換を阻止する方法
こんにちは。とあるフロントエンドエンジニアです。
今回はVite、Astro環境でWeb開発をすると切っても切り離せない問題、「css(scss)に書いた日本語フォントがビルド時にUnicodeエスケープシーケンスに変換されちゃう問題」に対する暫定的な対応策について書いていこうと思いました。
同じような悩みを持っているエンジニアの解決のヒントになればと思います。
Vite(Astro)でデフォルトの設定で圧縮する際の課題
例えばプロジェクトで以下のような日本語フォントをcss(scss)に設定していたとします。(これ以降はcssもscssも特にやることは変わらないので全てcssとして表記を統一します。)
body {
font-family: "ヒラギノ丸ゴ ProN";
}
そしてデフォルトのVite環境でビルドすると、font-familyに設定したフォントの日本語部分が以下のようにUnicodeエスケープシーケンスに変換され圧縮されてしまいます。なんか嫌ですね。。
/* ビルドするとこうなる */
body{font-family:\30d2\30e9\30ae\30ce\4e38\30b4 ProN}
/* ほんとはこうであってほしい */
body{font-family:"ヒラギノ丸ゴ ProN"}
ブラウザは基本的にこれらのようなUnicodeエスケープシーケンスは解釈できるので、問題ないっちゃないのですが、一部のフォントはUnicodeエスケープシーケンスに変換されると効かなくなってしまい、ブラウザで正常にフォントが適用されなくなったりするので、可能な限りビルド後のこの変換は避けたいところです。
加えてUnicodeエスケープシーケンスで変換されたcssファイルは開発者・運用者にとってとても見づらく、可読性が下がるというデメリットも考慮すると、できれば日本語が含まれたフォントはそのままビルドできるようにしたいなとずっと思っていました。
その他cssプロパティも同様
contentやlist-style-typeなどの日本語を入れる可能性のあるcssプロパティも同様に圧縮時の変換対象になります。
変換前
.sample::before {
content: "日本語だよ";
}
ul {
list-style-type: "オリジナルマーカー";
}
変換後
.sample:before{content:"\65e5\672c\8a9e\3060\3088"}
ul{list-style-type:"\30aa\30ea\30b8\30ca\30eb\30de\30fc\30ab\30fc"}
ただ先ほども記載したように、ブラウザはUnicodeエスケープシーケンスを解釈できるので、これらは変換されてもブラウザでは問題なく表示されます。
なのでフォント以外のプロパティとなるとUnicodeエスケープシーケンスに変換される問題はそこまでクリティカルな問題ではないかもですね。
Unicodeエスケープシーケンスに変換された後のデメリットまとめ
- フォントによっては変換されると効かなくなる場合がある
- ビルド後のcssファイルの可読性↓
Astroも同様の課題
AstroもベースにViteが使われているので、Astro環境でビルドしてもcssに書いた日本語は同様に変換されてしまいます。
原因
原因はVite内部のesbuildによって行われるcssのminifyによるものです。
なのでこのプロセスをどうにかハンドリングしなければなりません。
そしてなぜcssもscssも対応策のプロセスとしては変わらないのかというと、Unicodeエスケープシーケンスに変換されるのはscssをcssにトランスパイルした後に圧縮される過程で変換されるため、scssのようなプリプロセッサを使っていても対応策のプロセスに変わりはありません。
暫定的な対応策
結論から言うと、暫定的な対応策として挙げられるのは、
"minifyはViteに任せず、自分で設定する"
です。
そこで今回は、
- cssはpostcssのプラグインであるcssnanoでminifyする
- jsはesbuildでminifyする
という方法でそれぞれ圧縮を行うよう設定していきます。
Vite環境で解決する方法
1. build.minifyをfalseに設定
vite.config.jsのbuild.minifyをfalseに設定し、Viteで勝手にやってくれるjs・cssの圧縮を解除します。
export default {
build: {
minify: false,
};
2. cssnanoをインストール
pnpm i -D cssnano
3. postcss.config.jsを設定
ルートディレクトリにpostcss.config.jsを作成し、cssnanoを以下のように設定。
const cssnano = require("cssnano");
module.exports = {
plugins: [
cssnano({
preset: [
"default",
{
minifyFontValues: {
removeQuotes: false,
},
},
],
}),
],
};
ここで、minifyFontValuesを指定しなかった場合、minify後のfont-familyは以下のように「""(ダブルクォーテーション)」が取り除かれてしまうため、これも設定しておきます。
/* minifyFontValuesがないとこうなる */
body{font-family:ヒラギノ丸ゴ ProN}
/* 理想 */
body{font-family:"ヒラギノ丸ゴ ProN"}
Viteは内部のpostcss-load-configによって作成されたpostcss.config.jsを検知してくれるため、どこかでpostcss.config.jsを読み込ませるための設定をする必要はありません。
4. esbuildでminifyするためのカスタムプラグインを作成
「1. build.minifyをfalseに設定」でbuild.minifyをfalseに設定すると、css, jsなど全てのminifyがされなくなるため、別でjsのminifyを設定する必要があります。
今回はカスタムプラグインを作成してjsを圧縮するコードを設定していきます。
まずesbuildをインストールします。
pnpm i -D esbuild
次に、vite.config.jsのplugins[]に以下のようにカスタムプラグインを作成
import { transform } from "esbuild";
export default {
build: {
minify: false,
},
plugins: [
{
name: "minify-js-with-esbuild",
apply: "build",
async generateBundle(_, bundle) {
for (const name in bundle) {
const file = bundle[name];
if (file.type === "chunk") {
const result = await transform(file.code, {
minify: true,
format: "esm",
charset: "utf8",
});
file.code = result.code;
}
}
},
},
],
};
今回研究段階で作成したterserでminifyするバージョンのコードも載せておきます。(お蔵入りは勿体無いなと思ったので。。。)
import { minify } from "terser";
export default {
build: {
minify: false
},
plugins: [
{
name: "minify-js-with-terser",
apply: "build",
async generateBundle(_, bundle) {
for (const name in bundle) {
const file = bundle[name];
if (file.type === "chunk") {
const result = await minify(file.code, {
format: {
comments: false,
},
});
file.code = result.code;
}
}
},
},
],
};
terserでminifyする場合は事前にterserをインストールしてください
pnpm i -D terser
ただterserの場合はesbuildよりもビルドに時間がかかります。Viteの公式も特に理由がない限りはesbuildでのminifyを推奨しています。
5. ビルドする
ここまで設定できたら以下コマンドでビルドしてみましょう。
pnpm build
日本語フォント、並びにその他プロパティで指定された日本語も正常に圧縮されているのが確認できると思います。jsもminifyされているのが確認できます。
body {
font-family: "ヒラギノ丸ゴ ProN";
}
.sample::before {
content: "日本語だよ";
}
ul {
list-style-type: "オリジナルマーカー";
}
body{font-family:"ヒラギノ丸ゴ ProN"}.sample:before{content:"日本語だよ"}ul{list-style-type:"オリジナルマーカー"}
Astro環境で解決する方法
続いてAstro環境でUnicodeエスケープシーケンス問題を解決する方法を記載していきます。
1. build.minifyをfalseに設定
astro.config.mjsのviteキーでviteの設定を編集することができます。
これはVite環境のvite.config.jsを修正するのと同じことを意味します。
export default defineConfig({
integrations: [],
vite: {
build: {
minify: false,
},
},
});
2. cssnanoをインストール
pnpm i -D cssnano
3. postcss.config.jsを設定
ルートディレクトリにpostcss.config.jsを作成し、cssnanoを以下のように設定。
const cssnano = require("cssnano");
module.exports = {
plugins: [
cssnano({
preset: [
"default",
{
minifyFontValues: {
removeQuotes: false,
},
},
],
}),
],
};
ここまではVite環境と同じです。
4. astro-compressをインストール
astroのコミュニティにcss, js, htmlなどを圧縮してくれるサードパーティのインテグレーション、「astro-compress」があるため、今回はこれをインストールします。
pnpx astro add astro-compress
すると自動的にastro.config.mjsにもインテグレーションの追記を行なってくれるので、インストール後はそのまま使えます。
import compress from "astro-compress";
// https://astro.build/config
export default defineConfig({
integrations: [compress()],
vite: {
build: {
minify: false,
},
},
});
このインテグレーションは以下記事を参考
5. ビルドする
これらの設定ができたら以下コマンドでビルドしてみましょう。
pnpm build
Astro環境でも同様に日本語フォントやその他日本語の値を含むcssのプロパティもUnicodeエスケープシーケンスに変換されることなくビルドできていると思います。
おわりに
今回はpostcssのcssnano、自分でインストールしたesbuildでcss・jsをそれぞれminifyするプロセスで問題の解決を図りましたが、
"minifyはViteに任せず、自分で設定する"
という大まかなロジックであれば他のプラグインなどでもいけると思っています。
この記事で同じような悩みを持っている方の問題解決のヒントになれば幸いです。
Discussion