🚀

【Vite・Astro】ビルド時の日本語フォントのUnicodeエスケープシーケンス変換を阻止する方法

2023/03/20に公開

こんにちは。とあるフロントエンドエンジニアです。

今回はVite、Astro環境でWeb開発をすると切っても切り離せない問題、「css(scss)に書いた日本語フォントがビルド時にUnicodeエスケープシーケンスに変換されちゃう問題」に対する暫定的な対応策について書いていこうと思いました。

同じような悩みを持っているエンジニアの解決のヒントになればと思います。

Vite(Astro)でデフォルトの設定で圧縮する際の課題

例えばプロジェクトで以下のような日本語フォントをcss(scss)に設定していたとします。(これ以降はcssもscssも特にやることは変わらないので全てcssとして表記を統一します。)

style.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の圧縮を解除します。

vite.config.js
export default {
  build: {
    minify: false,
};

2. cssnanoをインストール

pnpm i -D cssnano

3. postcss.config.jsを設定

ルートディレクトリにpostcss.config.jsを作成し、cssnanoを以下のように設定。

postcss.config.js
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を読み込ませるための設定をする必要はありません。
https://ja.vitejs.dev/guide/features.html#postcss

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[]に以下のようにカスタムプラグインを作成

vite.config.js
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するバージョンのコードも載せておきます。(お蔵入りは勿体無いなと思ったので。。。)

vite.config.js
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を推奨しています。
https://ja.vitejs.dev/config/build-options.html#build-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を修正するのと同じことを意味します。

astro.config.mjs
export default defineConfig({
  integrations: [],
  vite: {
    build: {
      minify: false,
    },
  },
});

https://docs.astro.build/ja/reference/configuration-reference/#vite

2. cssnanoをインストール

pnpm i -D cssnano

3. postcss.config.jsを設定

ルートディレクトリにpostcss.config.jsを作成し、cssnanoを以下のように設定。

postcss.config.js
const cssnano = require("cssnano");

module.exports = {
  plugins: [
    cssnano({
      preset: [
        "default",
        {
          minifyFontValues: {
            removeQuotes: false,
          },
        },
      ],
    }),
  ],
};

ここまではVite環境と同じです。

4. astro-compressをインストール

astroのコミュニティにcss, js, htmlなどを圧縮してくれるサードパーティのインテグレーション、「astro-compress」があるため、今回はこれをインストールします。
https://github.com/astro-community/astro-compress

pnpx astro add astro-compress

すると自動的にastro.config.mjsにもインテグレーションの追記を行なってくれるので、インストール後はそのまま使えます。

astro.config.mjs
import compress from "astro-compress";

// https://astro.build/config
export default defineConfig({
  integrations: [compress()],
  vite: {
    build: {
      minify: false,
    },
  },
});

このインテグレーションは以下記事を参考
https://zenn.dev/link/comments/29ee9846de12c3

5. ビルドする

これらの設定ができたら以下コマンドでビルドしてみましょう。

pnpm build

Astro環境でも同様に日本語フォントやその他日本語の値を含むcssのプロパティもUnicodeエスケープシーケンスに変換されることなくビルドできていると思います。

おわりに

今回はpostcssのcssnano、自分でインストールしたesbuildでcss・jsをそれぞれminifyするプロセスで問題の解決を図りましたが、

"minifyはViteに任せず、自分で設定する"

という大まかなロジックであれば他のプラグインなどでもいけると思っています。

この記事で同じような悩みを持っている方の問題解決のヒントになれば幸いです。

Discussion