TranscendケースでM.2 SSDドライブを作ってベンチマーク(MacBookPro環境用)


「WD 内蔵SSD 1TB WDS100T2B0B-EC 」と「Transcend USB3.1 M.2 SSD 外付けケース TS-CM80S」の組み合わせでを作ったので、ベンチマークをとっておきました。
購入検討の参考にしてください。

構成



SSDドライブ化ケースのオススメに上がっているSSD。M.2のSATAタイプはM.2専用(?)のものに比べて遅いとされていますが外付けにするので今回は安くて大きいものを買いました。

ケースはもうすこし安い2000円以下のものもありましたが、SSDも売っているメーカのものを選んで安全をとりました。
以前おなじ構成を検討した時は、M.2 SSD(SATAでない)を外付け化する方法はなかったのですが(というか内蔵PCI-Eに追加する方法すら無かった)、今はM.2の外付け化も可能なようです。



ベンチマーク

参考のため内蔵SSDドライブの速度を。

内蔵SSD

-変換噛ませケーブル1(TypeB経由)
-変換噛ませケーブル2(TypeA経由)
-HUB経由
ここでTypeB/Aは速度に関係なくて単に区別を付けるためのものです。

TypeC-ケーブル-TypeB-変換キャップ-TypeC

TypeB-TypeC変換キャップは100円均一のものでした。

TypeC-ケーブル-TypeA-ケース純正ケーブル
TypeC-TypeAケーブルは、以前秋葉原で300円くらいで買ったものです。




TypeC-電源&HTMI付きハブ-TypeAは、amazonで1000円ちょっとのものを買いました。



TypeC-ケーブル-TypeB-変換キャップ-TypeC / exfat


TypeC-ケーブル-TypeB-変換キャップ-TypeC / mac拡張fs

Mac TypeC-ケーブル-TypeA-ケース純正ケーブル-TypeC ケース /exfat



Mac TypeC-ケーブル-TypeA-ケース純正ケーブル-TypeC ケース /mac拡張fs

Mac TypeC-電源&HTMI付きハブ-TypeA-ケース純正ケーブル-TypeC ケース /exfat
HUB接続についてはtimemachineをセットしてからなのでexfatのみです。
上のケーブルと比べて全体に遅いですが早い項目もあって悩ましい。
(後述の仮想マシンを置く目的でそれほど遅くなかったようなので、今はこれで運用しています。)

MacOSXでの利用について。

 最初は使用できない旨の警告が出ますが、とりあえず適当なファイルシステムを作れば、あとはそれを消してパーティションをカスタマイズ可能です。
今回はセミ外付けデータ用としてexfat。 (仮想マシンのイメージなどを置きたい・Linuxなどからデータを移すため)
mac拡張ディスクを300GBほど切り出して、そちらをtimemachineのドライブにしました。
250GB中200GBくらい使っているMacOSXで、バックアップ作成にかかったのは2時間くらいでした。

CSSでグラデーションの上にPNG画像を一緒に使う

ページ背景で、透過png画像とbackground / linar-gradientを同時に使うトリック。

electron 8で使えたのでchrome 78で有効なはず。




```
 13 html{¬
 14 >-------background: linear-gradient(-30deg, rgba(190, 255, 190, 1), rgba(215, 255, 215, 1));¬
 15 }¬
 16 body{¬
 17 >-------background: url("image/sky.png") center center / cover no-repeat fixed¬

 18 }¬

```

htmlタグに背景色、bodyに透過PNG画像。
最初はグラデーションと画像でcssファイルを分けていたのが有効化のトリックかとも思ったが、そうではなかった。

注意:
image/sky.pngはcssファイルからの相対パスになっていた。
```
index.html
css + style.css
    + image/sky.png
```


stackoverflowのコメントによると画像とグラデーションの同時利用は回答時点では仕様策定中と書いてあるのだと思う。
https://stackoverflow.com/a/10881460

electron-packagerがフリーズした場合の対処

electron-packagerがフリーズした場合の対処。


```
> daisy_sequence@201812.2.0 pack:win /home/nuka/daisy_sequence/daisy_sequence
> npm run clean:win && electron-packager . --out=./release/win --platform=win32 --arch=x64 --icon='./image/icon.ico' --ignore='release' --ignore='object/' --ignore='obj/' --ignore='work/' --ignore='test/'


> daisy_sequence@201812.2.0 clean:win /home/nuka/daisy_sequence/daisy_sequence
> rm -rf ./release/win

```

とかで止まった時、npmキャッシュを除去すると解決する場合がある。
sudo rm -rf ~/.npm


```
> daisy_sequence@201812.2.0 pack:win /home/nuka/daisy_sequence/daisy_sequence
> npm run clean:win && electron-packager . --out=./release/win --platform=win32 --arch=x64 --icon='./image/icon.ico' --ignore='release' --ignore='object/' --ignore='obj/' --ignore='work/' --ignore='test/'


> daisy_sequence@201812.2.0 clean:win /home/nuka/daisy_sequence/daisy_sequence
> rm -rf ./release/win

Packaging app for platform win32 x64 using electron v6.0.12
Wrote new app to release/win/daisy_sequence-win32-x64
~/daisy_sequence
~/daisy_sequence/daisy_sequence/release/win/daisy_sequence-win32-x64 ~/daisy_sequence
  adding: LICENSE (deflated 41%)
  adding: LICENSES.chromium.html (deflated 83%)
  adding: README.md (deflated 36%)
  adding: chrome_100_percent.pak (deflated 8%)


```


また、各プラットフォーム向けelectonバイナリのダウンロードは進行していてプログレスバーが出ていないだけの場合もある。
(macosx catalinaにて発生)
/tmp下を見るとわかる。electron-*的な名前のファイルが存在してサイズが増えていくはず。
`ls -lh /tmp`


技術書典5「クロス本」売り上げ報告ほか

この記事は技術同人誌 その2 Advent Calendar 2018 Advent Calendar 2018 の17日の記事です。




以上、売り上げ報告でした。
以下雑文。



本書、タイトルが長いので「クロス本」と呼んでます。以下ではそう書きます。
...本当は、クロス本執筆の動機を説明するのにちょうどよいため本文から「はじめに」章を転載しようと考えていたのですが、もしかすると参加アドベントカレンダーのレーティングに抵触する可能性が。わたしはGTK大好きです本当なんです。でなきゃアプリ開発に2年も投入しないし本も書かない。信じて。
そんな殺伐ポエム4Pを含むけれど他のページは本当に実用的な知見の塊「クロス本」は、電子書籍版をPixiv/BOOTHgubloadで買えます。






以下、気持ち。




だいたいの時系列。
- 参加を決めたのはサークル募集が始まってから。
- 何か新しい開発をする+それを書く、はつらい。既にやったことを書くほうが執筆時コストが少なく文章へ割けるリソースが増える。(思い出すというコスト、思い出せないというリスクは有る。)




- 内容は早くに時系列エッセイからTIPS集へ。そのほうが前後関係という依存関係が減り章がモジュール化されるため、書くのが楽になる。
- 原稿はMarkdownで書いてpandocベースの自作コンバータでPDF化。







- 入稿が初めてで一番安い締め切りは逃した。
- 日本語リンタは今回のクロス本の執筆には役立たなかった。
- 知り合いに原稿データを配って試し読みしてもらった。感謝。
- Web上での試し読み募集には応募がなかった。自分は別の方の本のレビューに参加させていただいた。
- チェック数と売上データのグラフを作っている方がいて、とても参考になったので自分もチェック数と売り上げを公開した。





こんなこともしていました。あまり反響無かったですが。



敵に囲まれておる、と思った。



- 確かサークル一覧公開から5日後くらいに、「この時期のブックマーク数の3倍の冊数が売れます」って言っている人がいたが、自分の場合は実際にそうなった。
- 売り子の約束をしていた友人が所要で1ヶ月前に自分1人で参加することになった。
- 別の知人が買い物ついでにお昼頃の店番をしてくれることになったので、買い物はその間にさせてもらった。

初めての技術同人誌、そして物理書籍の物理物販は、自分にとって大きな挑戦の機会でした。
またリリースされたクロス本は、今年の主要で輝かしい「進捗」となりました。

ことのはアムリラートには感謝してます。

今年は主要な進捗としてクロス本の他に、「daisy-cell-block-editor」をリリースしたいと考えています。
クロスプラットフォーム・デスクトップ・アプリケーションを扱ったクロス本に登場したクロスプラットフォームUMLシーケンス図エディタdaisy-sequenceの姉妹アプリケーションです。当然クロスプラットフォーム。


ご期待下さい。

seleniumテストの失敗、デバッグと引数と解決

seleniumテストが失敗する問題のデバッグと解決
====

# TL;DR
関数名のtypoを修正すると、それまで通っていたテストが落ちる。
原因は、テストシステムが生成して渡した不正な引数による異常系終了だった。
対処として、テストケース実行を汚い実装で検出して特別に処理することで問題を回避することとした。


# 問題
## 詳細
electron製アプリのdaisy-sequenceに対して、selenium/mochaでウィンドウの起動テストを行っている。
これはほぼselenium公式サンプルと同じシンプルなテストである。

daisy-sequenceではコマンドライン引数を受け取る。
コマンドライン引数のパースは`js/cli.js` の `class Cli.parse(argv)`にて行っている。
この中で、`sprintf-js`パッケージの`sprintf()`関数をtypoしていた。
```js
        const sprintf = require('sprintf-js').sprintf;
(略)
        if(2 < argv.length){
            if(argv[2] !== '-o'){
                arg.err = {
                    'message': spirntf('invalid argment. `%s`', argv[2])
                };
                return arg;
            }

```

これを修正したところ、ウィンドウが即座に終了し、seleniumテストに失敗する。
```bash
make unit-test
make[1]: ディレクトリ '/home/nuka/daisy_sequence' に入ります
cd daisy_sequence && npm run test test/

> daisy_sequence@201812.2.0 test /home/nuka/daisy_sequence/daisy_sequence
> mocha --require intelli-espower-loader "test/"



  Array
    #indexOf()
      ✓ should return -1 when the value is not present

  Application launch
    1) shows an initial window
    2) "after each" hook for "shows an initial window"


  1 passing (3s)
  2 failing

  1) Application launch
       shows an initial window:
     chrome not reachable
  Error: Request timed out after the element was still found on the page.
      at windowHandles() - application.js:240:17

  2) Application launch
       "after each" hook for "shows an initial window":
     chrome not reachable
  Error: Request timed out after the element was still found on the page.
      at windowHandles() - application.js:246:17



^CTerminated
Makefile:20: recipe for target 'unit-test' failed
```

# デバッグ

前提として、typoを修正するとテストに失敗する。
別のtypoを入れる、alart()を挿入する等すると、テストに成功するようになる。 // alart()を入れるのは不正なのか?
 > これまでテスト失敗が起こらなかったのは、不正で止まることが原因だったらしい。
arg.errへの代入(arg.errはエラーフラグを兼ねる)を消すとテストに成功する。
 > arg.errへの代入がおかしい? それとも、parse()がエラーを返すと失敗する?
Cli.parse()エラー時の`process.exit(-1);`を消すと、テストに成功する。
 > parse()がエラーを返すとテストが失敗する!
ところでprocess.exit()するとウィンドウが即座に閉じる。ウィンドウが閉じればテストは失敗する。
つまり不正な引数を受け取っていることが原因の可能性がある。

引数を確認する。
console.error()系, process.write.stderr()等を使用しても出力されない。
// 上記デバッグの際もこれが作業の困難さを増した。
対処として、単にファイルに書き出すこととした。
`fs.writeFileSync("daisy-arg", JSON.stringify(remote.process.argv));`
結果、取得できたselenium/mocha実行時のコマンドライン引数。
```js
["/home/nuka/daisy_sequence/daisy_sequence/node_modules/electron/dist/electron","/home/nuka/daisy_sequence/daisy_sequence","--disable-background-networking","--disable-client-side-phishing-detection","--disable-default-apps","--disable-hang-monitor","--disable-popup-blocking","--disable-prompt-on-repost","--disable-sync","--disable-web-resources","--enable-automation","--enable-logging","--force-fieldtrials=SiteIsolationExtensions/Control","--ignore-certificate-errors","--load-extension=/tmp/.org.chromium.Chromium.oi97WO/internal","--log-level=0","--metrics-recording-only","--no-first-run","--password-store=basic","--remote-debugging-port=12106","--test-type=webdriver","--use-mock-keychain","--user-data-dir=/tmp/.org.chromium.Chromium.h85Kss","data:,"]
```
daisy-sequenceは、第二引数が存在する場合に`-o`でなければ、不正なコマンドライン引数として即座に失敗終了する。
(ただし、先頭がelectronで終了する場合これを引数に数えない。)

原因は不正な引数を受け取っていたことによる不正終了により、ウィンドウが即時終了していたことであると考えられる。

## 現象
大筋は以下。
typo修正前の動作はこのようになっていた。
- selenium/mocha実行はelectronに対してテストに適したコマンドライン引数を渡し、アプリケーションはそれを受け取る。
- アプリケーションはtypoにより「未知の関数を実行しようとした例外」で停止する。
- selenium/mocha実行は途中でアプリケーション側に例外が発生しても検知(失敗判定)しない。
- seleniumはウィンドウが起動している・終了できたことに満足してテストをOK判定する。
以上のことからseleniumによるウィンドウ起動テストは成功していた。
なお、「不正な引数を受け取ったことによる不正終了」と「未知の関数を実行しようとした例外」はどちらもアプリケーションの不正終了を起こす。他のテストでは不正な引数をわたしたことによる不正終了が起こりその理由の区別はつかないので、テスト成功となっていた。

typoを修正したことで、以下のように変化した。
- selenium/mocha実行時のコマンドライン引数を受け取ることで、アプリケーションは不正な引数エラーで即座に不正終了する。
- seleniumはウィンドウが起動していないためテストをNG判定する。

// ところで「不正終了」という言葉の使い方が正しくない気がする。まあいいけど。

# 対処
コマンドライン引数からそれらしい引数を検出したらselenium実行と判定してコマンドライン処理をskipすることとした。
キー文字列は"--test-type="にした。

# 今後の課題
- selenium/mocha実行は途中でアプリケーション側に例外が発生しても検知(失敗判定)しないが、検出して失敗判定するようにしたい。
 daisy-sequenceは幸い、正常系で(起動時にwarningが一件出るのと、編集削除操作でエラーログを出力する以外に)例外が発生しない。
- アプリケーション側でパースする際にelectron向けコマンドライン引数を適切に排除する。

electronアプリnode cliアプリでのコマンドライン実行の種類とparseの共用について

必要なelectron,nodeのCLIについての周辺情報の収集・整理を行った。
(CLIというとユーザ入力のプロンプトを含むが、今回はプロンプトを含まないコマンドライン引数によるワンショット実行をCLIと呼びそれを扱う。)

# 要求仕様
daisy-sequenceおよびdaisy-cell-block-diagram(以下daisyシリーズ)は本物のシステム開発アプリケーションを志向している。
本物のシステム開発アプリケーションはCLI、CI連携可能なコマンドラインインタフェースを持つ。
本稿ではdaisyシリーズのCLIについて、以下の通り仕様と実装を検討し、必要なelectron,nodeのCLIについての周辺情報の収集・整理を行った。


## 画面仕様
usage: `app [input_filepath [-o output_filepath]]`
- guiとcli(コマンドライン)を提供する。コマンドラインインタフェースを共通とする。
    - GUI,CLIの区別は別バイナリ提供により実現する。
    - output指定の有無によるGUIとCLIそれぞれの挙動の違いについては未規定とする。
    - (GUIではoutput指定があると不正終了または書き出して終了、CLIではoutputが無ければ不正終了、を想定している。)
- helpとversionはあるに越したことはないが無くていい。
- win/mac/linux対応。
    - win,linuxのランチャに対応するため、GUIのオプション第一引数は渡されたファイルを開く。

## 機能仕様
- electronとnode(cli)で可能な限りコードを共有したい。せめて設定パラメタは共有したい。

# electorn, nodeのCLIの基礎
一般的なコマンドラインでは、先頭がアプリケーション実行ファイルの起動パスで後ろにコマンドライン引数が付く形で実行し、アプリに渡されるコマンドライン引数(argv)も同じである。
しかし、node, electronではそうでない場合がある。

### nodeから起動する場合と、npmパッケージのCLIコマンドとして実行する場合では引数の数が変わる
1. `node ./path/myapp.js myarg1 myarg2`
2. `npm run myapp -- myarg1 myarg2`
3. `node_modules/.bin/myapp`


`node`コマンドによる実行では、頭に`node`が付く。開発中などに用いる。
package.jsonコマンドによる`npm run`実行では、一般的なコマンドライン引数が渡される。
(`--`を付けることで以降の引数が`npm run`から実行アプリケーションに渡る。
 [npm run (npm run-script) コマンドで実行するスクリプトに引数を渡す。2017]( https://qiita.com/tiny-studio/items/ce28bf84c76aba53122f ) )
package.jsonのbin要素による実行ファイル実行では、一般的なコマンドライン引数が渡される。npm配布パッケージとしてインストールした後などに用いる。

```js
 10 >-------// コマンドライン引数を[1]からに調整(node実行の場合に必要)¬
 11 >-------let argv = process.argv;¬
 12 >-------if(argv[0].endsWith('node')){¬
 13 >------->-------argv.shift();¬
 14 >-------}¬
```

### electronで起動する場合と、ビルド後のアプリでは引数の数が変わる
1. `npm run myapp myarg1 myarg2`
2. `./myapp.exe myarg1 myarg2`


package.jsonコマンド起動では、頭に`electron .`と引数2個が付く。electronで起動するためである。開発中などに用いる。
ビルド後のアプリケーションでの実行は、一般的なコマンドライン引数が渡される。

```js
  63 >-------let argv = remote.process.argv;¬
  64 >-------if(argv[0].endsWith('electron') && argv[1] === '.'){¬
  65 >------->-------argv.shift(), argv.shift();¬
  66 >-------}else{¬
  67 >------->-------argv.shift();¬
  68 >-------}¬
```

### (備考) macosxの場合
本編ではないが。
macosxでファイルタイプに対するデフォルト起動アプリケーション登録によるファイルOpen(winのexplorerやLinuxのランチャ)は第一引数によらない。
`open-file`eventで受ける。念の為。
```js
app.on('open-file', openFileHandler);
```

### コマンドラインパーサの呼び出し位置
nodeのコマンドラインパーサパッケージを使う場合、そのうちのいくつかは、electronの'ready'イベントの前にMainProcessで使用する必要があると思われる。
基本的に、electronのRenderProcess側のわりと特殊な環境を想定していない模様。
具体的にはargsをremoteから取る部分、console.log()出力など。

[electronにおけるコマンドライン引数の位置とパーサ]( https://qiita.com/akameco/items/cfbde2f1b241e2babc6c )

command-line-args を使ってエラーで失敗していたのだが、RenderProcess側でやっていたのが問題であった可能性がある。

ただし、後述の通りglobalな変数を使用せずアプリケーションからargvを渡す形のインタフェースであればこれは問題とならないし、daisyシリーズではこれを採用する。


# 再度、要求仕様
- node,electronで共用するにあたり、node,electronおよび呼び出し方法によるCLIの違いを吸収できる必要がある。
  具体的には、これら先頭オプションの違いを取り除く前処理を行ったargvを渡せる必要がある。
  (例えば`argv`パッケージはargv引数を取らないので落選となる。)

minimistを用いるか、規模的に独自実装するのが適切と考える。


# vscodeの場合
vscodeではmain.jsでminimistをめちゃくちゃ単純なオプションで使用している。
後処理でもっと複雑なことをしているような気もするが、調査していない。

```js
/**
 * @returns {ParsedArgs}
 */
function parseCLIArgs() {
    const minimist = require('minimist');

    return minimist(process.argv, {
        string: [
            'user-data-dir',
            'locale',
            'js-flags',
            'max-memory'
        ]
    });
}
```

## 引き渡し
MainProcessからRenderProcessへの引き渡しは、ShareObjectを使えばよさそう。

main.js
```js
  8 global.sharedObject = {'osx_open_file': null};¬
  9 let openFileHandler = function(event, path) {¬
 10 >-------event.preventDefault();¬
 11 >-------global.sharedObject.osx_open_file = path;¬
 12 };¬
```

js/index.js
```js
  85 >-------if(typeof remote.getGlobal('sharedObject').osx_open_file === 'string'){¬
  86 >------->-------if(null === arg.open_filepath){¬
  87 >------->------->-------arg.open_filepath = remote.getGlobal('sharedObject').osx_open_file;¬
  88 >------->-------}¬
  89 >-------}¬
```

## 先頭ファイルパスの実現
minimistは本当にコマンドラインパーサをするだけ。help(usage)は作ってくれない。
よって、先頭ファイルパスを除いてそれより後ろのargvを渡せばよい。`argv.shift()`よりも`argv.slice(2)`がよくサンプルコードに書かれる。


electronアプリをbrowserifyしてAndroid移植したmemo

現実世界のelectronでnodejsな趣味コードをbrowserifyした際の知見。
====

世の中のまともなプロjsエンジニアは最初からボイラープレートでgulpなビルドパイプラインを構築してまっさらなプロジェクトにjsxコードをおもむろに書き始めるとかしているのだと思います。
しかし現実のワークフロー、というか開発フロー、もとい移植フローというのはそんなに綺麗なものではありません。
例えばelectronのためにnode.js的なmodule.exportsで書いていた移植性を考えていないjsコードをどうにかブラウザもといWebViewで動かしたい向きもあるわけです。
つまり私です。個人プロジェクトなんですよlina_dictoは。
(あ、移植性はちょっと考慮してた。そこは嘘になっていますが嘘でなくて機能追加の際に一度移植性について諦めたのです。)





成果物:
https://github.com/MichinariNukazawa/lina_dicto_for_android_2018


# 前置き
electronで書いたエスペラント辞書アプリケーション lina_dicto をAndroidのWebViewに移植したのが lina_dicto_for_android です。
以前は、
- electron側のコードを出来るだけ、require()などのnode固有機能を使わない、ピュアなブラウザjsで書く
- ファイルIOなどどうしてもブラウザで出来ない部分はMakefileによるビルドプロセスでファイル上書き・置換等を行う
など、ある程度移植を意識したelectronコードを書くことで対処してきました。
しかしunittestにワンクッション入れたり、機能追加が面倒だったりしたため、lina_dicto(electronデスクトップ版)はこの前の機能追加の際にこの方針を諦めました。
そのためAndroid版の更新追従は諦めなければならなかったのですが、そうは言っても新機能は魅力的で、Android版での提供を諦めきれるものではありませんでした。
というわけで、electron向けコードをbrowserifyでブラウザjsに変換する方向で対処してみた次第です。

## browserifyがlocalなnodejsモジュールを読み込まない

lina_dictoのディレクトリ構成は下記の通り。require()さえ解決すればブラウザ実行できてしまう構成になっています。
```
lina_dicto/
    index.js        # electronのエントリポイント
    index.html      # メインページ(ピュアHTML)
    js/
        index.js    # 辞書アプリケーションのエントリポイント(HTMLページに読み込まれる)
        *.js
        ...         # その他jsファイル(scriptタグで読まれるブラウザjsやrequire()で読み込むmodule.exportsモジュールが混在)
```

lina_dictoは js/index.js が主要機能のエントリポイントで、以下の通りrequire()で`module.exports`を使ったローカルなjsファイルのモジュールを読み込んでいます。
```js
  1 'use strict';¬
  2 ¬
  3 var extension = new Extension();¬
  4 var platform = new Platform();¬
  5 // const Language = new Language();¬
  6 const Language = require('./js/language');¬
  7 const Esperanto = require('./js/esperanto');¬
  8 const Dictionary = require('./js/dictionary');¬
  9 let dictionary = new Dictionary();¬
 10 const Linad = require('./js/linad');¬
 11 let history = new History();¬
 12 ¬
 13 var timeline_item_id = 0;¬
 14 let dictionary_handle = null;¬
 15 ¬
 16 window.onload = function(e){¬
 17 ¬

```

しかしこれをこのようにbrowserify にかけると、
`node ./node_modules/browserify/bin/cmd.js js/index.js -o bundle.js`
下記エラーが出ます。

```js
lina_dicto/$ node ./node_modules/browserify/bin/cmd.js js/index.js -o bundle.js
Error: Cannot find module './js/dictionary' from '/home/nuka/lina_dicto_for_android/app/src/main/assets/lina_dicto/js'
    at /home/nuka/lina_dicto_for_android/app/src/main/assets/lina_dicto/node_modules/browser-resolve/node_modules/resolve/lib/async.js:55:21
    at load (/home/nuka/lina_dicto_for_android/app/src/main/assets/lina_dicto/node_modules/browser-resolve/node_modules/resolve/lib/async.js:69:43)
    at onex (/home/nuka/lina_dicto_for_android/app/src/main/assets/lina_dicto/node_modules/browser-resolve/node_modules/resolve/lib/async.js:92:31)
    at /home/nuka/lina_dicto_for_android/app/src/main/assets/lina_dicto/node_modules/browser-resolve/node_modules/resolve/lib/async.js:22:47
    at FSReqWrap.oncomplete (fs.js:152:21)
```
悩ましいのが`Cannot find module `のmoduleがビルド毎にランダムに変わることです。わけがわからない。多分中で並列ビルドなりなんなりしているのだと思いますが。
ともあれ解決は以下のとおりです。

```
mv js/index.js .
node ./node_modules/browserify/bin/cmd.js index.js -o bundle.js
```
つまりは相対パスの解釈の問題らしいです。
これlina_dictoでは偶然これで解決できるパス構成だったからよかったものの、もっと複雑なパスを組んでるアプリケーションだったらパスの書き換えまで必要だったのでは?

## scriptタグで読んだブラウザjsコードから、browserifyしたfunctionを呼ぶことができない
早速ブラウザで実行すると、下記エラーで止まりました。
```js
platform.js:9 Uncaught ReferenceError: query_input_element is not defined
    at Platform.init (platform.js:9)
    at window.onload (bundle.js:26)
```

該当箇所のコード
```js
  3 class Platform{¬
  4 >-------init()¬
  5 >-------{¬
  6 >------->-------// 検索ボタンを有効化¬
  7 >------->-------let button = document.getElementById("query-area__query-input__button");¬
  8 ¬
  9 >------->-------button.addEventListener("click", query_input_element, false);¬
 10 ¬
 11 >------->-------return true;¬
 12 >-------}¬
```

nodeモジュール化して一緒にバンドルすれば解決するかとおもったのだが、ならなかった。
```js
bundle.js:2625 Uncaught ReferenceError: query_input_element is not defined
    at Function.init (bundle.js:2625)
    at window.onload (bundle.js:26)
```

仕方ないのでlina_dicto本体コードに直接追加した。
```js
   53 >------->-------{¬
   54 >------->------->-------// browserifyで別ファイルから関数を指定できない問題を、¬
   55 >------->------->-------// 解決するのが面倒だったため、ここへ処理を追加¬
   56 >------->------->-------if('android' === Platform.get_platform_name()){¬
   57 >------->------->------->-------// 検索ボタンを有効化¬
   58 >------->------->------->-------let button = document.getElementById("query-area__query-input__button");¬
   59 >------->------->------->-------button.addEventListener("click", query_input_element, false);¬
   60 >------->------->-------}¬
   61 >------->-------}¬
   62 >-------});¬
   63 }¬
```
lina_dicto本体コードに不要コードが増えたことになるが、コードの美しさがlin_dictoのゴールではない。この程度なら許容範囲とした。

## browserifyしたコードから、バイナリファイルを読み込めない

```js
bundle.js:10347 Access to XMLHttpRequest at 'file:///node_modules/kuromoji/dict/base.dat.gz' from origin 'null' has been blocked by CORS policy: Cross origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, https.
```

https://developer.mozilla.org/ja/docs/Web/HTTP/HTTP_access_control/Errors/CORSRequestNotHttp?utm_campaign=default&utm_medium=firefox-cors-errors&utm_source=devtools

エラーチェックを緩和することで関係部分以外の基本的な辞書動作はブラウザで確認できる。そこから先をchromiumでのこれ以上の動作確認は諦めて、Androidでやることに。

Android WebViewではCORSを緩和できる。
```
        webView1.getSettings().setAllowFileAccessFromFileURLs(true);
```
https://developer.android.com/reference/android/webkit/WebSettings
https://stackoverflow.com/questions/4666098/why-does-android-aapt-remove-gz-file-extension-of-assets

Androidのassetから拡張子 .gz ファイルは取り除かれる。
これは拡張子を変えることで回避できる。
```bash
 cd node_modules/kuromoji/dict/
 rename 's/\.gz/.bin/' *
```
どうやら.gz.binでは駄目で、.gzを除く必要がある。


## HTMLから、browserifyしたfunctionを呼ぶことができない

イベントコールバックの登録に、HTMLへ書き込む古い方法を使っていました。
```html
    <input id="query-area__query-input__input" type="text"¬
        value='wakeup...' disabled='true'¬
        onkeypress="on_keypress_by_query_input_element(event);" onkeyup="on_keyup_by    _query_input_element(event);" />¬
```

しかしこれは下記エラーを起こす。
```js
index.html:27 Uncaught ReferenceError: on_keypress_by_query_input_element is not defined
    at HTMLInputElement.onkeypress (index.html:27)
onkeypress @ index.html:27
index.html:27 Uncaught ReferenceError: on_keyup_by_query_input_element is not defined
    at HTMLInputElement.onkeyup (index.html:27)
```

index.js(bundle.js)でaddEventListener()するよう書き換えて処置した。
```js
 52 >------->-------{¬
 53 >------->------->-------let input = document.getElementById("query-area__query-input__input");¬
 54 >------->------->-------input.addEventListener("keypress", on_keypress_by_query_input_element, false);¬
 55 >------->------->-------input.addEventListener("keyup", on_keyup_by_query_input_element, false);¬
 56 >------->-------}¬
```

## その他
AndroidStudioのエミュレータで日本語入力
https://blog.webarata3.link/android-emulator-japanese

WebView側でスクロール管理して画面いっぱいに表示されたい場合に、Layoutを挟みサイズのオプションもしなければならなかったのが、WebViewを貼り付けてデフォルト設定で適用すればOKになっていた。
(古い設定は使えなくなっていた。)

タイトルバーの消し方が変わっていた。前の方法(androidmanifest.xmlのandroid:theme)は使えなくなっている模様。
http://ntoshi1900.hatenablog.jp/entry/2018/01/21/%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AE%E3%82%BF%E3%82%A4%E3%83%88%E3%83%AB%E3%83%90%E3%83%BC%E3%82%92%E6%B6%88%E3%81%99%EF%BC%88Android_Studio_3.0.1%EF%BC%89

###
ソフトウェアキーボード表示時にアクティビティ(WebView)がリサイズで追従しない問題。
タイトルバーを消すときに、不要な`<item name="android:windowFullscreen">true</item>`を指定していたのが原因。
```
        <item name="windowNoTitle">true</item>
        <item name="windowActionBar">false</item>
        <item name="android:windowFullscreen">true</item>
        <item name="android:windowContentOverlay">@null</item>
```

```
        <activity android:name=".MainActivity"
            android:windowSoftInputMode="adjustResize"
```
`android:windowSoftInputMode="adjustResize"`は不要だった。

https://github.com/delight-im/Android-AdvancedWebView/issues/113

TranscendケースでM.2 SSDドライブを作ってベンチマーク(MacBookPro環境用)

「WD 内蔵SSD 1TB WDS100T2B0B-EC 」と「Transcend USB3.1 M.2 SSD 外付けケース TS-CM80S」の組み合わせでを作ったので、ベンチマークをとっておきました。 購入検討の参考にしてください。 構成 ...