Kuromoji.js を Vite で動かす
JavaScript 上で日本語の形態素解析を実現する有名なライブラリに、kuromoji.js が存在する。これを Vite(+ React)の環境下で使用させようとすると、ブラウザのコンソールに以下のエラーが表示される。
(1) development 環境(yarn run dev
)にて
Error: invalid file signature:60,33
at $.g (kuromoji.js?v=6d025e79:1081:44)
at xhr.onload (kuromoji.js?v=6d025e79:4777:30)
(2) production 環境(yarn run preview
)にて
index-DQDeigRE.js:40 Uncaught TypeError: Cannot read properties of undefined (reading 'Gunzip')
at n.onload (index-DQDeigRE.js:40:120830)
解決策
kuromoji.js は最終更新が 6 年前であり、メンテナンスされていない。したがって、(1), (2) ともにパッチを当てて解決する。
パッチの適用には、patch-package パッケージを使用する。yarn で利用する場合は postinstall-postinstall も併せてインストールする。
yarn add patch-package postinstall-postinstall
package.json
に以下の記述を追加。
"scripts": {
"postinstall": "patch-package"
}
(1) への対処
Vite の public ディレクトリに .gz ファイルを配置すると、データが欠損する現象が報告されている。
そのため、辞書データの拡張子を .gz
から .gzip
に変更して public ディレクトリに配置する。
また、/node_modules/kuromoji/src/loader/DictionaryLoader.js
の .gz
を .gzip
に置換する。以下のような記述が 12 箇所あるので、これを全て置換する。
- async.map([ "base.dat.gz", "check.dat.gz" ], function (filename, _callback) {
+ async.map([ "base.dat.gzip", "check.dat.gzip" ], function (filename, _callback) {
(2) への対処
Chrome 80 から、gunzip に相当する機能が Compression Streams API として標準で提供されているため、zlibjs の代替としてそちらを利用する。
/node_modules/kuromoji/src/loader/BrowserDictionaryLoader.js
の以下の箇所を置換する。
// zlibjs は使用しないのでインポートを削除
- var zlib = require("zlibjs/bin/gunzip.min.js");
// zlibjs の代わりに Compression Streams API を用いる
- var gz = new zlib.Zlib.Gunzip(new Uint8Array(arraybuffer));
- var typed_array = gz.decompress();
- callback(null, typed_array.buffer);
+ const ds = new DecompressionStream("gzip");
+ const blob = new Blob([arraybuffer]);
+ const decompressedStream = blob.stream().pipeThrough(ds);
+ new Response(decompressedStream).arrayBuffer().then(buffer => {
+ callback(null, buffer);
+ });
パッチの適用
以下のコマンドを実行してパッチを作成し、kuromoji.js を再インストールする。
yarn patch-package kuromoji
yarn install
パッチ作成時、/patches/kuromoji+0.1.2.patch
には以下のファイルが作成される。
kuromoji+0.1.2.patch
diff --git a/node_modules/kuromoji/src/loader/BrowserDictionaryLoader.js b/node_modules/kuromoji/src/loader/BrowserDictionaryLoader.js
index 04bfdcd..302f59d 100644
--- a/node_modules/kuromoji/src/loader/BrowserDictionaryLoader.js
+++ b/node_modules/kuromoji/src/loader/BrowserDictionaryLoader.js
@@ -17,7 +17,6 @@
"use strict";
-var zlib = require("zlibjs/bin/gunzip.min.js");
var DictionaryLoader = require("./DictionaryLoader");
/**
@@ -47,9 +46,13 @@ BrowserDictionaryLoader.prototype.loadArrayBuffer = function (url, callback) {
}
var arraybuffer = this.response;
- var gz = new zlib.Zlib.Gunzip(new Uint8Array(arraybuffer));
- var typed_array = gz.decompress();
- callback(null, typed_array.buffer);
+ // zlibjs の代わりに Compression Streams API を用いる
+ const ds = new DecompressionStream("gzip");
+ const blob = new Blob([arraybuffer]);
+ const decompressedStream = blob.stream().pipeThrough(ds);
+ new Response(decompressedStream).arrayBuffer().then(buffer => {
+ callback(null, buffer);
+ });
};
xhr.onerror = function (err) {
callback(err, null);
diff --git a/node_modules/kuromoji/src/loader/DictionaryLoader.js b/node_modules/kuromoji/src/loader/DictionaryLoader.js
index 5f88c0b..ed69460 100644
--- a/node_modules/kuromoji/src/loader/DictionaryLoader.js
+++ b/node_modules/kuromoji/src/loader/DictionaryLoader.js
@@ -47,7 +47,7 @@ DictionaryLoader.prototype.load = function (load_callback) {
async.parallel([
// Trie
function (callback) {
- async.map([ "base.dat.gz", "check.dat.gz" ], function (filename, _callback) {
+ async.map([ "base.dat.gzip", "check.dat.gzip" ], function (filename, _callback) {
loadArrayBuffer(path.join(dic_path, filename), function (err, buffer) {
if(err) {
return _callback(err);
@@ -67,7 +67,7 @@ DictionaryLoader.prototype.load = function (load_callback) {
},
// Token info dictionaries
function (callback) {
- async.map([ "tid.dat.gz", "tid_pos.dat.gz", "tid_map.dat.gz" ], function (filename, _callback) {
+ async.map([ "tid.dat.gzip", "tid_pos.dat.gzip", "tid_map.dat.gzip" ], function (filename, _callback) {
loadArrayBuffer(path.join(dic_path, filename), function (err, buffer) {
if(err) {
return _callback(err);
@@ -88,7 +88,7 @@ DictionaryLoader.prototype.load = function (load_callback) {
},
// Connection cost matrix
function (callback) {
- loadArrayBuffer(path.join(dic_path, "cc.dat.gz"), function (err, buffer) {
+ loadArrayBuffer(path.join(dic_path, "cc.dat.gzip"), function (err, buffer) {
if(err) {
return callback(err);
}
@@ -99,7 +99,7 @@ DictionaryLoader.prototype.load = function (load_callback) {
},
// Unknown dictionaries
function (callback) {
- async.map([ "unk.dat.gz", "unk_pos.dat.gz", "unk_map.dat.gz", "unk_char.dat.gz", "unk_compat.dat.gz", "unk_invoke.dat.gz" ], function (filename, _callback) {
+ async.map([ "unk.dat.gzip", "unk_pos.dat.gzip", "unk_map.dat.gzip", "unk_char.dat.gzip", "unk_compat.dat.gzip", "unk_invoke.dat.gzip" ], function (filename, _callback) {
loadArrayBuffer(path.join(dic_path, filename), function (err, buffer) {
if(err) {
return _callback(err);
以上の修正により、development, production 環境の両方で kuromoji.js が正常に使用可能となる。