最近の JS 遊び … 中国語判定、初期表示、マルコフ連鎖による文章生成、ギタータブ譜生成など
最近の Web フロントエンド、Node.js 関連の成果物を一気に紹介。
目次
- Detect Chinese
- Convert Encoding New Line
- Watch Events
- Huge Initial Load
- XREA に MovableType・WordPress・MediaWiki を置いた
- マルコフ連鎖 npm パッケージの比較
- Tab2Tab
- Neo's Validator
Detect Chinese
文字列が「中国語かどうか」をゆるく判定するスクリプト。
- 過去記事 : 文字列が「中国語かどうか」を判定したい
過去に作りかけのコードを放置していたので、とりあえず npm パッケージ化してみた。
そしたらプルリクをいただいて、Unicode 抽出部分のバグ取りや TypeScript 定義などを追加いただいた。スバラシキ!
npm パッケージとしてグローバルインストールすれば CLI ツールとして、Node.js スクリプトから require()
したりして、あとはブラウザ上でも動作するようにと、UMD 形式で作ってあって、型定義は type.d.ts
で提供している。ブラウザ上で動作するデモは以下をドウゾ。
Convert Encoding New Line
テキストファイルの文字コードや改行コードを判定したり、変換したりする Node.js 製の CLI ツール。コチラも npm パッケージとして公開できるようにした。
CLI としてのオプション部分は Commander でパースしてサボった。
文字コードや改行コードは Bash 標準の file
コマンドなんかでも確認できるし、nkf
コマンドをインストールすればコマンドラインでの変換もできる。別段新しいツールではないのだが、自分が使いたいように使えるコマンドがなかったので、とりあえずデッチ上げた。
今回は encoding-japanese
というパッケージのみ利用しているが、それ以外は利用していない、JavaScript コードのみで動いている。なので、もしかしたらやろうと思えばブラウザ上でファイルアップロードしたら変換後のファイルをダウンロードさせる、みたいなスクリプトにもできるかもしれない。でも今回は面倒臭いので CLI ツールとしてのみ動くようにして作っている。w
- Neos21/convert-encoding-newline: テキストファイルの文字コード・改行コードを判定・変換する CLI ツール
- @neos21/convert-encoding-newline - npm
Watch Events
window
や document
、Element などが持っているイベントの一覧を見てみたくて作った。
それぞれのオブジェクトから、on
から始まるプロパティ名を拾ってきて、それらを利用して addEventListener()
を仕込んでいるだけ。click
とか mouseenter
とか wheel
(マウスホイールイベント) とか、対象のイベントが発火したらそのイベント名を赤くハイライトしている。
そういえば DOMContentLoaded
イベントはプロパティとしては存在していないのね。document.onDOMContentLoaded
みたいなプロパティは存在しなかったようで、デモページの一覧に出てきていない。
Huge Initial Load
ページを初期表示する際に、重たい同期処理が行われたら、画面描画がどんな風になるのかを調べるため作った。いわゆる「JavaScript はシングルスレッドで動く」という、イベントループの話である。
- デモ : Huge Initial Load
- 画面初期表示時に
for
ループを大量に繰り返しているので、ページを開くのに数秒かかります - どうしても開けない場合は、リンク先 URL の末尾に
?l=0
とつけてみてください。for
ループによる負荷をかけなくなります
- 画面初期表示時に
- コード : frontend-sandboxes/index.html at master · Neos21/frontend-sandboxes
僕は普段、head
要素内に script
要素を書くが、その場で処理を実行させることはなくて必ず document.addEventListener('DOMContentLoaded')
の中で処理を開始するようにしている。つまり、HTML 全体の読み込みは終わって、画面初期表示が終わってから JavaScript コードが動くはずなのだが、重たい同期処理があると HTML 部分だけの初期表示が行われず、真っ白な画面がしばらく続き、同期処理の完了とともに初期表示がなされる、という挙動をしていた。
また、初期表示時に API をコールしてデータを取得する、というような非同期処理が発生する場合もあると思う。そのような非同期処理と、SPA フレームワークなどが大量の DOM 操作をするとか、JS 内で計算処理をするとかで、時間のかかる同期処理が重なる場合もあるだろう。そういう時に、どんな風に画面が初期表示されるのか、どういう書き方をしたらファーストビューが速く、全体的な処理完了までが速いのかを調べてみた。
このデモページでは、「for
ループを大量に回す同期関数」と「setTimeout
で待機してから resolve
する非同期関数」の2つを用意してある。そしてそれらを直列で実行したり、並列実行してみたりしている。同期関数の方は Promise でラップしてみたり、setTimeout
でラップしてみたりして、どういう変化があるか確認できるようにした。
デモページの中断で、そうしたパラメータを指定してページを開けるようにしている。いつものようにクエリ文字列でそうしたオプション指定ができるようにしてある。直列実行の順番とか、Promise.all()
の配列に関数を列挙する順番とかも指定できるので、どういうコードの書き方をしたらどういう画面描画になるのか、というのを、開発者コンソールの出力やソースコードなども見ながら確認してみてほしい。
自環境で目視確認したところ、概ね次のような結果になった。
- 重い同期処理があると、HTML を初期表示するだけのレンダリングもブロックされてしまい、真っ白な画面がしばらく続いてしまう
- 重い同期処理が終わったタイミングで初期表示のレンダリングと同時に画面描画がされるような挙動になる
- 重い同期処理を
setTimeout
でラップすることで、初期表示のレンダリングを先に終わらせて、真っ白な画面の時間を短くできそう- 重い同期処理を Promise のみでラップするのは効果がない
- Promise 内で
setTimeout
を重ねるのは、setTimeout
でのラップだけと同じなので、Promise ラップ自体は全く影響を及ぼさないことが分かった
setTimeout
で実行開始タイミングを遅延させても、その重たい同期処理が始まると、結局は並列動作している非同期処理の方もブロックされてしまう- コレにより、非同期処理の方は「API 通信が終わっていて、あとは DOM 更新して結果を表示するだけ」みたいなタイミングでも、画面描画が待たされるような動きになったりする
- 「軽めの非同期処理」と「重い同期処理」があるなら、「重い同期処理」の開始を
setTimeout
で少し遅延させ、先に「軽めの非同期処理」を開始させた方が、体感の初期表示は速く感じられると思う
というワケで、「とりあえず HTML 部分だけは先に表示させてしまいたい」のであれば、重たい同期関数を setTimeout
でラップすれば良いことが分かった。時間は 0
でも問題ない。
そして、そういう同期処理と非同期処理を並列実行させようとしても、シングルスレッドで動作する JavaScript の世界ではこれらを同時に・並列して動かすことはできないので、タイミング次第では結局非同期関数の処理がブロックされる。なので Promise.all()
を使ったりして並列化しても意味はない。
それよりは、非同期関数を先に呼び出しておき、同期関数の開始タイミング (setTimeout
の値) を 0
ではなく、もう少し大きな数値にして開始タイミングを遅らせてしまうことで、先に非同期関数の処理が全て終わって画面描画が終わり、それから同期関数の処理が開始する、という順番にしておいた方が、「何も表示されない真っ白な画面」が映る時間は短くできると思う。
XREA に MovableType・WordPress・MediaWiki を置いた
XREA サーバは昔から CMS をワンクリックでインストールできるツールが管理画面に用意してあったのだが、今回改めてそれをやってみた。試したのは neo.s58.xrea.com というスペースにて。
管理画面で MySQL データベースを作る。XREA Free の場合はデータベース名は選べず、アカウント名と同じデータベース名となる。
あとは管理画面で CMS をインストールする。「WordPress」「MovableType」「Xoops X」の3つがインストールできるのだが、Xoops X については PHP のワーニングが絶えず表示されていて使い物にならなさそうだったので断念した。インストール時にサブディレクトリ名を指定するので、WP と MT を両方インストールしたりもできる。
インストールが終わったら、それぞれのパスにアクセスすることで初期設定が始められる。WordPress は色々イジった経験があるけど、MovableType は超久々だった。管理画面からブログを複数個作れるようで面白いね。それぞれの UI が2000年代初頭の感じがあって懐かしい。w
MediaWiki については、XREA サーバに SSH 接続して、自分でインストールした。
# SSH 接続した直後は cd コマンドなどが制限されているので、その場合はさらに Bash を起動すれば回避できる
$ bash
$ cd ./public_html/
# MediaWiki の最新版をダウンロードする。64MB 程度だった
$ wget https://releases.wikimedia.org/mediawiki/1.38/mediawiki-1.38.2.zip
$ unzip ./mediawiki-1.38.2.zip
$ mv mediawiki-1.38.2 mediawiki
というワケで、/mediawiki
にアクセスすると初期設定ウィザードが開く。各種設定を勧めていくと、LocalSettings.php
というファイルがダウンロードできる。コレを ~/public_html/mediawiki/
直下に配置 (scp
コマンドでアップロードするなど) すれば初期設定完了。
あ、この際、「サイト設定」で設定されている PHP のバージョンが php70
などの古いバージョンだと、インストール要件を満たしていなくてうまく動かなかったりする。自分は php74
を選択して数分待ってリトライすることでうまく行った。
あとはアカウント名にハイフンが入っていると、各種アプリケーションが MySQL のデータベース名としてうまく認識できずにセットアップが失敗するので、その場合はアカウント名を変更するしかない。以下のヘルプページを参考に申請をすれば、アカウント名を変更してもらえる場合がある。
あと、MediaWiki の方はどういうワケか XREA の自動挿入広告が入らなかったので、自分で挿入した。見様見真似でテンプレートをイジって対処。
$ vi ~/public_html/mediawiki/skins/Vector/resources/skins.vector.styles.legacy/layouts/screen.less
/* 次の2つの要素についてプロパティ値を変更して、画面上部に隙間を開けた */
#mw-head-base {
margin-top: -5em; /* 元の値は -1em */
}
#mw-head {
top: 0; /* 元の値は 4em */
}
$ vi ~/public_html/mediawiki/skins/Vector/includes/templates/skin-legacy.mustache
<div id="mw-head-base" class="noprint"></div>
<!-- 元のコードは ↑ のようになっている。コレを以下のように変更した -->
<div id="mw-head-base" class="noprint">
<div id="xrea-ad" style="position: absolute; top: 0; right: 0;">
<!--nobanner-->
<script type="text/javascript" src="https://cache1.value-domain.com/xa.j?site=【アカウント名】.s0.xrea.com"></script>
</div>
</div>
XREA の広告コードは、管理画面 (コントロールパネル) の「契約情報」より取得できる。
コレで、MediaWiki の画面右上に XREA の広告を配置できて、XREA Free の規約も守れただろう。
作ってみたかっただけで、これらのブログに何か書く予定もないし、Wiki を書いていくつもりもあまりない。w
マルコフ連鎖 npm パッケージの比較
以前、Python と MeCab を使ってマルコフ連鎖による文章自動生成を試したことがあった。
- 過去記事 : マルコフ連鎖で「しゅうまい君」的な文章を自動生成してみた
- 過去記事 : GPT2 再挑戦して WSL で日本語文章を自動生成できた
- 文章自動生成でいうと他に GPT2 も試したことがある
その時は MeCab というライブラリを使って分かち書きをしていた。
- 過去記事 : Python MeCab CGI : MeCab パース処理を提供する Python CGI を作ってみた
- Python MeCab CGI … 今はココに置いたのでコチラで試せます
日本語の場合、普段は分かち書きをしないのだが、マルコフ連鎖プログラムに文章を投げ込む時は分かち書きをしておくことでより自然な文章が生成できる。
そこで今回は、主に JS オンリーで分かち書きとマルコフ連鎖処理を実現できそうな npm パッケージをいくつか探して試してみた。
結果やソースコードなど一式は ↑ に置いた。
夏目漱石の「吾輩は猫である」をインプットテキストにしたのが良くなかったか、やはり分かち書きの精度は MeCab が一番高いかもしれない。以下にそれぞれのスクリプトを複数回実行して出力した文章を貼っておく。
- js-markov を利用
- 日本語文章を分かち書きせずに投げ込んでおり、出力に関しても「文字数」を指定する形なので、途中で切れたような結果になっている
「アハハハハハハハもうたいていても好いから、この講話を真面目になったものではない。多々良三平のような
「君も乱暴狼藉の練修に余念のない男である。第一からかうのは本当の人間の命と云う事に極めたかを逐一かい
「ええこれなざあ面白いんでしまったが、まだ主人夫婦の間、嫁姑の間に截然たるかたで行くつもりに違ないで
「しかしよくありますよ」と主人は例のごとく眉の根にちょっと逃げ出すかも知れぬことをいってすましてね。
「すべての安楽は困苦を通過せざるを得ない。誰も聞きます。始めはちょっとボール即ち攻城的砲術である。鼻
- @hideokamoto/markov-chain-kuromoji を利用
- kuromoji.js で分かち書きしている。辞書ファイルの読み込みに時間がかかるようで、今回試した中では一番動作に時間がかかった
どこへ行ったり、酢に漬けて見なすったんだわ。
御嬢さまも、止せと云って出られっこないよ。
ああ奇麗に光っているはずだと気がついたと云わば吾輩猫などになる工夫はあるときは、この点に少しは淋しいだろうじゃないけども、淑徳婦人会の連発にて当分の間我慢して自己の災なるのみならずに上った女は罪な者でも利きましょう」とちゃらちゃら鳴らして見せるから安心したフロックコートを至急送れと云う句を細君に食っている小児ですらこのあき地、とか何とか威張っているものの実際はやはり実験室で珠を磨いてるのかい。
苦労と心配した覚はございませんでしたら小使でもよろしゅうございますので……」はてねと御相談なさるものですから能く聞き給え」と顔を洗いに風呂場へ行ったって食い物に不自由はした昨夜のごとく黄を含める淡灰色に漆のごときはまさしくその現象のあらわれる時代です、先生に御願があって来たら自分の顔を即席にこしらえて見るのです……つまり身振りがあまり見事に焼けてい給えと云ったって、先は商売だから恐縮しまさあ。
時に寒月はどうだい苦沙弥諸先生の御名論をやりますと云い放った。
- markov-chain-mecab を利用
child_process.exec()
でmecab
コマンドを呼び出す、mecab-async
パッケージを裏で使っている- MeCab に渡すテキストが長すぎると処理に失敗してしまうようなので、インプットファイルを短くちぎって渡している。データが少ないので文章のバリエーションはイマイチかもしれないが、文章の自然さは一番マシかも
はてな何でも同じ事を四五遍繰り返したのである。
書生がまた迎に来てくれるかと考え付いた。
そこを我慢してやって見た。
眼をねぶって運を天に任せていなかったから別段恐しいとも思わなかったから眼をねぶって運を天に任せていた。
のみならず顔の真中があまりに突起して無理やりに這って行く。
- @hideokamoto/markov-chain-tiny を利用
- TinySegmenter で分かち書きしている。分かち書き自体の精度が一番良くなさそう
しかし今に主人はいつでも大きな奴が平生の領分は吃驚して仕事を提げている士官も申されたでしょう。
こんな大きな木板でまんまと首尾よくまた。
たしかに分配していると一般の話をしているところが総身のは、自己の鼠でも有んなさるの事だけ聞いたら大変儲かったの鼻のはどう云う感じているごとくえらくも、この春朗読会へ引越すまんからんね ] その人に呼びつけて 。
人間である以上は君、今日のうち、惜気も挫しぐ上げたら直ぐ飛び出す用意であるものだ 君なかから少し馬鹿竹にはそうは樟脳をつづけた。
口髯をしないがある。
今回改めて思ったけど、「しゅうまい君」のような面白い文章を自動生成させるには、インプットとなる文章の量とバリエーションが必要だ。小説なんかを利用してもあまり面白い文章は出てこない。マルコフ連鎖処理よりも、投入するテキストをいかにクロールするか、という方が大事そうだ。w
Tab2Tab
自分はギターのタブ譜を書く時に、Guitar Pro とか Power Tab とかは使わず、プレーンテキストでメモしている。MIDI として再生する予定もなく、完全に個人のメモなので、プレーンテキストの方がソフトに依存しなくて気楽なのである。ただ、プレーンテキストでタブ譜を書くとなると、ハイフン記号などで位置を調整するのがダルい。
そこで、執筆する際は「Excel 方眼紙」で1マスごとにフレット数などを書き込んで行くことにし、それをプレーンテキストとして変換するためのツールを作ったという次第。
例によって UMD 形式で作ったので、Node.js CLI ツールとして使えたり、require()
できたり、ブラウザ上でも動くようにしてある。完全にオレオレなパッケージ。w
Neo's Validator
JS で実装している時によくやるバリデーション系の関数をまとめておきたいなーと思って npm パッケージ化してみた。
- Neos21/neos-validator: Neo's Validator
- @neos21/neos-validator : Neo's Validator : UMD … UMD として読み込んでいるデモページ
- @neos21/neos-validator : Neo's Validator : ESM … ESModules として読み込んでいるデモページ
TypeScript ベースで作っておいて、CommonJS (CJS・Node.js の require()
で読み込める形式) と UMD (ブラウザの window
配下にプロパティが生える昔ながらの方式)、あと ESModules (ESM・<script type="module">
から import
で読み込める方式) の3つの方式でビルドするようにした。
- 参考 : 複数のモジュール形式(CommonJS, ES Modules, UMD)をサポートしたnpmパッケージの作り方 in TypeScript - dackdive's blog
- 参考 : zaki-yama/typescript-npm-package-template: A template for developing npm package that supports multiple module format(CommonJS, ESM, UMD)
作ってる途中で飽きて放置してたのだが、とりあえずパッケージとして公開するところまでやった。多分このまま放置かな。w
以上。こんなモノを作って遊んでおりました。