技術
約3分で読めます
kuromoji.jsのpath.join()バグとCDN辞書読み込みの解決策
やりたかったこと
Labページに日本語の形態素解析ツールを追加したかった。品詞情報付きで。
最初の選択肢:Sudachi
SudachiはWASM版があるが、辞書ファイルが50MB以上。Vercelの転送量を即座に食い尽くすので却下。
次の選択肢:kuromoji.js
kuromoji.jsは辞書が約12MB(gzip圧縮時)で比較的軽量。辞書をjsDelivrのCDNから配信すれば、Vercelの帯域を消費しないはず。
kuromoji.builder({
dicPath: 'https://cdn.jsdelivr.net/npm/kuromoji@0.1.2/dict/'
}).build((err, tokenizer) => {
// ...
});
ローカルでは動く
開発環境では問題なく動作。しかし本番ビルドすると…
本番で404エラー
GET https://lilting.ch/cdn.jsdelivr.net/npm/kuromoji@0.1.2/dict/base.dat.gz 404
なぜかlilting.ch/cdn.jsdelivr.net/...という相対パスになっている。
原因:path.join()がURLを壊す
kuromoji.jsは内部でNode.jsのpath.join()を使って辞書パスを組み立てている。
// kuromoji.js内部
path.join(dicPath, 'base.dat.gz')
path.join()はファイルシステム用の関数なので、URLのhttps://の二重スラッシュを正規化してしまう:
path.join('https://cdn.jsdelivr.net/dict/', 'base.dat.gz')
// → 'https:/cdn.jsdelivr.net/dict/base.dat.gz' // スラッシュが1つ消える
結果、ブラウザはこれを相対パスとして解釈し、現在のドメインからの相対パスになる。
これはGitHub Issue #37で報告されている既知のバグ。
解決策:@patdx/kuromojiでカスタムローダー
@patdx/kuromojiはkuromojiのフォークで、カスタムローダーをサポートしている。
pnpm add @patdx/kuromoji
カスタムローダーを実装して、path.join()を完全に回避:
import * as kuromoji from '@patdx/kuromoji';
const CDN_DICT_BASE = 'https://cdn.jsdelivr.net/npm/kuromoji@0.1.2/dict/';
// gzip解凍
async function decompressGzip(data: ArrayBuffer): Promise<ArrayBuffer> {
const ds = new DecompressionStream('gzip');
const stream = new Response(data).body!.pipeThrough(ds);
return new Response(stream).arrayBuffer();
}
const customLoader: kuromoji.LoaderConfig = {
async loadArrayBuffer(filename: string): Promise<ArrayBufferLike> {
const url = CDN_DICT_BASE + filename;
const res = await fetch(url);
if (!res.ok) throw new Error(`Failed: ${url}`);
const data = await res.arrayBuffer();
// .gzファイルは解凍が必要
return filename.endsWith('.gz') ? decompressGzip(data) : data;
}
};
const tokenizer = await new kuromoji.TokenizerBuilder({
loader: customLoader
}).build();
ポイント
- 直接fetch:
path.join()を使わず、文字列連結でURLを組み立てる - gzip解凍:辞書は
.gz圧縮されているので、ブラウザのDecompressionStreamAPIで解凍 - Vercel帯域ゼロ:辞書はjsDelivrから配信されるので、Vercelの転送量を消費しない
まとめ
| 方法 | 問題 |
|---|---|
| Sudachi WASM | 辞書50MB+、重すぎ |
| kuromoji.js + CDN | path.join()バグでURL壊れる |
| @patdx/kuromoji + カスタムローダー | 動く |
ブラウザで形態素解析を実装する場合、kuromoji.jsは便利だがCDNから辞書を読み込む場合はpath.join()問題に注意。フォーク版を使ってカスタムローダーを実装するのが確実。
→ 形態素解析ツール