PowerShell に移行しつつある
これまではちょっとなにかしたいというときはとりあえず Node.js だった
だけど今でも Node.js は標準機能だけだと簡単なことでも不便なところがある
例えば http(s) リクエストとか

package.json を用意して色々インストールする前提だと困らないレベルだけど ちょっとしたスクリプトだけでパッケージのインストールが必要になるのはなんかイヤ
パッケージ内であっても メインの処理ではない極稀に使うことがある便利ツールくらいなもののために追加パッケージを入れるのも同じく避けたい

そういうのだとグローバルインストールがありかもしれないけど グローバルインストールはコマンド以外は避けたくもある
パッケージ側でインストール漏れてても動いてしまうせいで あとになって必要なパッケージの記載漏れに気づくとかあるし 初めて実行する環境だと事前準備でインストールが必要だし

Node.js にこだわる必要もないので他のツールを探すと PowerShell が良さそう
デフォルトで色々できるし Windows だと標準で使える
ちょっとしたスクリプトを動かしたい環境って基本は開発環境の Windows であって サーバ側で動かすようなものならちゃんとパッケージに含めた機能になるはず

PowerShell だとコマンド呼び出して結果のこの部分取り出して別のコマンドに渡して その結果によって次のコマンドを変えたり とかそういうのがすごくやりやすい
zx が出てきた頃は それに移行を考えてたけど Node.js な分 標準機能だと不足機能がある問題があるし .NET が Linux でも動くので PowerShell に揃えていくのもありかなって思った
PostgreSQL の日付型は now という文字列が自動で現在時刻に変換される
現在時刻を設定したいけど アプリケーションサーバのタイムスタンプじゃなくてデータベースサーバのタイムスタンプに統一したいところ
Node.js で knex を使っていて knex だと現在時刻を設定したいところは pg.fn.now() を入れておけば良い

そのとき update するためのオブジェクトを作るところは別モジュールで pg を参照できなくて とりあえず "now" という文字列で渡しておいて update を実行するところで置換すればいいやと思ってた
その後 pg.fn.now に置き換える処理を追加するのを忘れてたけどなぜか普通に動いてる
調べてみると "now" という文字列を日付型に保存しようとすると自動で現在時刻として扱われるみたい
knex 固有の機能というわけではなくて psql で SQL を書くときでも一緒
単にキャストすればそうなる

postgres=# select 'now'::timestamp;
timestamp
----------------------------
2021-09-18 06:33:46.890335
(1 row)

すごく便利
pg.fn.now() なんて長いの書く必要なかった
React で state リセットするコンポーネントラッパー
React を使い始めてからずっと扱いづらいと思ってる部分の一つが state のリセット
コンポーネント内なら難しくないけど 外から受け取るデータに応じてのリセットがあまり考慮されてない気がする
そういう使い方を推奨してないんだろうけど やっぱりそういうことしたいときは結構出てくる

特に コンポーネント内の途中の状態を変更のたびに親に伝えるのは無駄だし 親の処理が増えてくる
ページのコンポーネントになってくると 子孫コンポーネントの状態更新の処理があれこれ混ざって複雑になってる
やっぱり理想はコンポーネントの中での操作が完了してから初めて親に伝えるもの
外から受け取ったデータを state に保持して 完了時に onXXX などで受け取った関数を呼び出すだけだからこれは普通にできる
問題なのは 2 回目以降
state は引き継がれるから前回の情報がコンポーネントに残ったまま

外から受け取るデータが変更されたタイミングで state をリセットしたいので こういう方法で対処してた

const Component = ({ value }) => {
const [state, setState] = useState(value)

useEffect(() => {
setState(value)
}, [value])

return (
...
)
}

これの欠点は 変更時に 1 回ムダにレンダリング処理が行われること
それと useState が多いと setState みたいな関数を何度も呼び出さないといけないところ
コンポーネント内でリセット機能があってすでに関数を用意してるならそれを呼び出せばいいけど
あと useEffect のこういう使い方って正しいの?と思うところもある

その他の方法は key を使うこと
親コンポーネントが props を変えるときに key も変えればリセットできる
でもこれはコンポーネントを使う側に 追加でしてもらうことが増えてあまり良い方法じゃないと思う
key の変え忘れでバグになることが多そう

それで思ったのがコンポーネントを 2 重にして key の制御もコンポーネント側でやる方法

const ComponentInternal = ({ value }) => {
const [state, setState] = useState(value)

return (
...
)
}

const Component = (props) => {
const key = useMemo(() => Math.random(), [props.value])
return <ComponentInternal key={key} {...props} />
}

結構いい感じ
useMemo を使って Math.random() を返すことで依存プロパティが変わったときに key を変えることができる
変わったときにコンポーネントをリセットしたいプロパティを useMemo の依存プロパティに設定する

都度書くのも面倒なのでもう少し楽に作れるように

const resetStateComponent = (Component, deps) => (props) => {
const key = useMemo(() => Math.random(), deps.map(d => props[d]))
return <Component key={key} {...props} />
}

を用意しておいて 作るときは元にするコンポーネントと依存プロパティの名前の配列を引数で渡す

const Component = resetStateComponent(ComponentInternal, ["value"])

key を変える以上 コンポーネント全体を作り直すので リスナをつけていて一瞬でも解除したくないとか API の fetch をしていてリセットのタイミングで毎回 fetch したくないとかあるなら 外側コンポーネントを自作してリセットしたくないデータはそっちで準備して props で渡すか key を使わず state リセットする方法にしたほうが良さそう
Node.js の http サーバで keep-alive を無効にする
リクエストは API のみで HTML のあとに CSS とか JavaScript とかをロードすることはないサーバ
同じクライアントからのリクエスト頻度はたまにあるくらい
keep-alive のメリットが特にないし keep-alive することで前書いたようなサーバを即時止めるために少し手間が必要になる
これは keep-alive を無効でいいかと思ったけどドキュメントを見ても無効化する方法が載ってない

server.keepAliveTimeout というのがあるけどこれはタイムアウトの設定だから 0 にしてもタイムアウトしなくなるだけ
たぶんクライアントが切断するまでつないだままになる

調べると レスポンスの HTTP ヘッダで 「Connection: close」 を送ればいいというのがあったけど 直接ヘッダ操作はあまりしたくない
ソースコードを見てると shouldKeepAlive というプロパティがあって これを見てヘッダの Connection を追加してる
https://github.com/nodejs/node/blob/v14.17.6/lib/_http_outgoing.js#L436

HTTP 1.0 だと false になってて HTTP バージョンによって自動で判断されてる
このプロパティを false にすれば無効にできそう

http.createServer((req, res) => {
res.shouldKeepAlive = false
res.end("ok")
})

これで試してみるとレスポンスは 「Connection: close」 になって ちゃんと無効化できてた
at メソッドの引数は整数に変換されてるみたい
at メソッドの引数の扱いが話題になってました

最近追加された配列や文字列の at メソッド
最後の要素など後ろからのインデックスを使ったアクセスで便利な機能です
整数以外の値を渡したときは変換されてから扱われるようです

"abcd".at(null)
// a
"abcd".at(undefined)
// a
"abcd".at(1.9)
// b

null や undefined は 0 で 1.9 は 1 と扱われてる結果になってます
「~~value」 でキャストしてそうですが

~~Infinity
// 0

"abcd".at(Infinity)
// undefined

となってるあたり 完全にこの方法ではないようです

[n] でのアクセスと同じではなく .slice(n)[0] のシンタックスシュガーらしく slice を使えば Infinity は文字列なら空文字で配列なら空配列になりそうですし そう言われるとわかりやすいと言えます
「"abcd".at(index)」 というコードを見ると [index] と同じ動きになりそうに見えるのでわかりづらい感はありますが 整数以外を渡すことはまずないですし 実装ミスのときにどう動くかなのでどっちでもいいところです
想定外の動きだったときの原因探しのために 頭の片隅に記憶しておいたほうがいいかもくらいですね

自分が使うケースを考えてみても 基本は -1 リテラルがほとんどで その他は index や length と足し算引き算したくらいで整数が以外が入ることはまずないです
仮に入る可能性があっても at に整数以外を入れる意味がないので ~~index で整数化して渡すので動作的には一緒です
それにこれまでは .slice(-index)[0] だったので at に置き換えた場合の挙動はこれまでと同じになります

見たときの直感とは違いますが この挙動でいいんだとは思います
PowerShell は ps1 ファイルを実行したときに同じプロセスで実行される
Linux のシェル風に考えていて それぞれ別プロセスだと思ってたのに同じだった

$pid

を a.ps1 に保存して PowerShell で実行する

PS C:\Users\user9\Desktop> $pid
16640
PS C:\Users\user9\Desktop> .\a.ps1
16640
PS C:\Users\user9\Desktop> & .\a.ps1
16640
PS C:\Users\user9\Desktop> powershell .\a.ps1
7948

最初のが実行元の PID で直接 ps1 ファイルを指定したり & を使ったりしても同じ PID が表示される
コマンドに powershell を指定すれば PowerShell プロセスが起動するので別の PID

変数のスコープが終わってるし 困ることないかと思ってたけど GC されないことで Excel プロセスが残るとか カレントディレクトリのずれが引き継がれるとかあった

echo "pw cwd" (Get-Location).path
echo ".net cwd" ([IO.Directory]::GetCurrentDirectory())

cd でカレントディレクトリ移動後に↑のスクリプトを実行すると

pw cwd
C:\Users\user9\Desktop\dir1\dir2
.net cwd
C:\Users\user9\Desktop

スクリプト内では cd してないから PowerShell と .NET のカレントディレクトリが一致してると思いきや 同じプロセスなのでスクリプトを実行してる環境のずれが引き継がれてる

こういうのがあるし 基本は別プロセスで実行するようにしたほうがよさそう
React の input でユーザ入力完了後に処理したい2
前回のつづき
一旦完結はしたんだけど チューニング関係でやっぱり change イベントがほしいなと思ったので

一文字入力ごとに state に同期させると input にテキスト入力するときにかなりの頻度で再レンダリングされる
タイプが早い人だと 1 秒間に何回も
input の再レンダリングだけなら何の問題もない程度だけど input の value に指定する state は親の親の親とか input より親のコンポーネントから渡されるのがほとんど
ページやアプリのルートに近いコンポーネントで管理してることが多い
そうなると 再レンダリング対象コンポーネントが多くなるし 次の文字入力時にまだ処理中ってこともある
処理が重いコンポーネントは作ったときにメモするようにしておけばいいけど 毎回それも面倒だし チューニングは遅いと感じたらする方針にしてることが多い
それでいざやるときにはあちこちのコンポーネントをメモ化することになってやる気が起きない
問題なのは再レンダリング頻度が異常に高い input の方なんだからこっちをどうにかすべきだと思う

ただ文字入力ごとに状態が変更されてるわけだし できることはやっぱり change イベントにして入力が一旦終わったところで反映するくらい
一文字ごとに状態を更新するメリットって入力途中なものをどこかに反映したり 入力チェックしたりできるくらいで あまり重要じゃないと思う
markdown エディタ兼プレビューツールみたいな input(textarea) からフォーカスを外さないようなのだと change イベントじゃだめだけど input イベントで毎回更新が必要というわけではなくて 変更があった場合に数秒に 1 回くらいの更新でいい
入力チェックは入力中にあれこれ言われたくなくて入力後にしてほしいって言う話を聞くことがあるし 私もどちらかというとその意見に賛成
なので input をラップするコンポーネント内では input イベントごとに state へ反映してもその外側のアプリケーションとしては change イベントなどのタイミングで更新するくらいで十分

こういう Input コンポーネントを用意しておくと良いかも

const Input = ({ value, onChange: notifyChange, ...props }) => {
const [state, setState] = useState("")

useEffect(() => {
if (state !== value) {
setState(value)
}
}, [value])

const onBlur = () => {
if (state !== value && notifyChange) {
notifyChange(state)
}
}

const onChange = (event) => {
setState(event.target.value)
}

return <input {...props} value={state} onChange={onChange} onBlur={onBlur} />
}

const App = () => {
const [state, setState] = useState("aa")
const onChange = (value) => {
console.log("changed")
setState(value)
}

return <Input value={state} onChange={onChange} />
}

二重に保持はしてるけど App コンポーネントにはフォーカス外れたときに反映される
React の input でユーザ入力完了後に処理したい
ユーザ入力を監視して 1 入力ごとに処理するのではなく 一旦入力が完了したタイミングで処理したい
空行や行頭行末の空白を消すなど ユーザの入力のフォーマット関係は入力途中でやると問題が出る

そういうときは change イベントで処理すればいいんだけど React だと onChange が input イベントのリッスンになってる
軽く調べた感じだと本来の change イベントをリッスンする機能は用意されてなさそう
ツールの方針的に常に state と DOM 状態を同期させるべきだから仕方ないのかも
Preact だとそういう余計な変更はしてないから onChange が使えるんだけど……

直接 input の参照を取得すればできるから ref と useEffect 使って addEventListener で直接リスナ設定かな
と思ってたけど blur イベントというのがあった
フォーカスが外れたときだから change イベントとタイミングは一緒
ただ 変更されてなくてもイベントが起きるけど
変更なしなら setState で何も起きないから 気にしないことにして onBlur で state に反映する

ユーザ入力完了後に小文字化する例

const App = () => {
const [value, setValue] = useState("")
const onChange = event => setValue(event.target.value.toLowerCase())

return (
<div>
<input defaultValue={value} onBlur={onChange} />
<div>input value: {value}</div>
</div>
)
}

defaultValue じゃなくて value で良さそうだけど value だと input に入力できなくなる
再レンダリングされてないから大丈夫そうなのに React は value がついてて onChange がついてない場合は入力を毎回リセットするみたい
バグ対策なのかもしれないけど迷惑

defaultValue にすると value による state の反映ができないから blur イベントが起きて state が更新されたあとは div のところは小文字化されてるけど input はユーザ入力そのままで大文字が入ってる
key をつけて state 更新時には input を再作成して defaultValue を反映させる

const App = () => {
const [value, setValue] = useState("")
const onChange = event => setValue(event.target.value.toLowerCase())

return (
<div>
<input key={value} defaultValue={value} onBlur={onChange} />
<div>input value: {value}</div>
</div>
)
}

key までついて複雑になってきたし value を onChange で state に保持して blur イベントで state を変換する方が単純かもしれない

const App = () => {
const [value, setValue] = useState("")
const onChange = event => setValue(value.toLowerCase())
const onInput = event => setValue(event.target.value)

return (
<div>
<input value={value} onChange={onInput} onBlur={onChange} />
<div>input value: {value}</div>
</div>
)
}

React だと input は必ず常に state と同期させるという前提で考えたほうがいいのかも
パイプライン演算子までにメソッド呼び出しの記法ができてほしい
これパイプライン演算子で書きたいなぁと思うような処理があって 置き換えたらかなりスッキリしそうと思って試しに置き換えてみた
スッキリはしたけどメソッド呼び出しが混ざると微妙になった
見た目的にはいいけど 書くのが思ったよりも面倒
例えばこういうメソッド呼び出しと関数呼び出しが混ざるもの

data.method()
|> fn1
|> fn2
|> value => value.method1()
|> value => value.method2()

「value => value.method2()」 が長いとは言えないけどもっと簡単に書きたい
全部をパイプラインで繋がずにメソッド呼び出しすればいいと思ったけど

(data.method() |> fn1 |> fn2).method1().method2()

あまり見やすくないしカッコが入るのがイマイチ
そういうのがなくフラットな見た目というのがパイプライン演算子を使う理由の一つになるのに これだとメリットがあんまりない

これはまだマシで関数呼び出しとメソッド呼び出しが交互に来ると

(((data.method() |> fn).method() |> fn).method() |> fn).method()

これはない
これなら最初のようにメソッド呼び出しする関数をアロー関数で都度作る方が何倍もマシ

以前どこかで書いたようなメソッド呼び出しする関数を作る機能を用意して $.method で x => x.method() の関数を作るようにしておけば

data.method()
|> fn
|> $.method
|> fn
|> $.method
|> fn
|> $.method

だけど毎回その関数を用意する手間もある
パイプライン演算子はまだ先だろうし そういう周辺機能的なのも充実してほしい

・・・と思ったけど考えてみると引数がない場合に限定しすぎてて 引数を設定するなら結局アロー関数になりそう
? か何かの記号を使って関数を作るパターンも考えられてたけど メソッドでも使えるんだっけ?
ドキュメントの PDF をテキストから作りたいけど
ドキュメントの PDF を出力したいけど Word は色々と辛い
スタイルが言うことを聞かなかったり バージョン管理や差分確認のしづらさがあるし 画像が多いと重くなったり 大きい画像の位置調整がしづらかったり
デメリットが多いけど 見たままだしマウス操作できるなどのメリットもあるしでとりあえず Word にしてたけどそろそろやめたい

テキストベースでドキュメントを書く記法だと markdown が有名でよく使われてる
HTML にすれば PDF 化もできる
だけど パーサごとに独自拡張とかあったりで記法が完全に統一されてない
ツール次第で出力が変わるものだと その場限りのものならともかく 長期間修正を繰り返す前提のものには使いづらい

方向的にはこれでいいので markdown に近いマークアップ言語を探してみる
markdown よりはしっかりした規格があるようなのだと AsciiDoc や reStructuredText が候補
AsciiDoc の方は記法があまりわかりやすくなく 直感だと reStructuredText の方が良さそう
だけど記法の詳細を見ると ソースコード上での見た目を重視な分 本文に合わせた装飾が必要みたい
見出しの下線や表のレイアウトなど
なんか日本語の全角と相性が悪そう

他のドキュメント記法は何かないか探していたら pandoc というツールを発見
これ自体は記法ではなく ドキュメント間の変換ツール
数十はあるドキュメントフォーマットを変換できる結構すごいものだった
名前は聞いたことあったけど使ったことなかった……
PDF への変換までサポートしてるみたいなので このツールの標準フォーマットを使えばいい気がする
と思って調べると 独自拡張の markdown だった
markdown は上に書いた問題あるしなー と思ったけど PDF 変換まで pandoc で行うなら pandoc 用の拡張 markdown に限定すれば別にいいような気もする
それなりにしっかりとしたツールだし その他 markdown 独自拡張やパーサみたいに気づけば消えていってるようなことはなさそうだし



で 使っては見たけど 色々問題が
本当にちょっとしたドキュメントの PDF 化ならすごく簡単だった
Docker のイメージが用意されてるので 説明どおりにすれば PDF 化までお手軽変換
ただし英語限定とスタイルがそのままでよければ

デフォルトだと latex を中間データにして PDF を生成していて その latex エンジンが日本語をサポートしてない
昔 latex テンプレートしか用意されてないフォーマットの PDF を作る必要があったときに latex を使ったときは platex という日本語用のバージョンを使ったはず
pandoc のデフォルトは pdflatex
platex はサポートしてなくて変わりに Unicode 対応の xelatex と lualatex なら使えるらしい

--pdf-engine=lualatex -V documentclass=ltjsarticle -V luatexjapresetoptions=ms

というオプションを pandoc に渡せば日本語も使えるらしいけど Docker 環境だと ltjsarticle が入ってないみたいでエラー
調べても Windows に texlive を scheme-full で完全インストールしておけば良いとかそういう前提で Docker 環境なら何を入れればよいかよくわからない

とりあえずコンテナ内で tlmgr で scheme-full をインストールしてみたけど full というだけあって重すぎて数時間かかるペース
しかも途中で Docker のホストの WSL ごと落ちて完了してない
メモリ不足とか?

とりあえず tlmgr のインストールで ltjsarticle を直接指定したらインストールできたらしい
他に足りないのがあれば都度エラーが出ると思うのでそのたびに入れればいいか という感じで

試してみると次はフォントがないというエラー
haranoaji というフォントが必要みたい
ms を指定してるのになぜ?
ipa とかに変えてもこのフォントが要求される
とりあえず tlmgr で入れる

これで一応 PDF は出るようになった……けど スタイル的に求めるのには程遠い
なんというか論文?とか出版物?にありそうな感じ
もうちょっと親しみやすい感じにしたい
見た目的には Word をちょっとカスタムしたくらいのでいいんだけど



スタイルを変えようとしたけど latex のスタイル調整なんてしたことないし マクロ構文が複雑すぎて読む気も起きない
あー CSS は偉大だなー

latex をマスターするつもりでやるならともかく サクッとスタイルだけ好みにしたいだけで何時間かかるかもわからない苦労はしたくない
デフォルトスタイルでいいとか スタイル定義がすでにあるわけでもないなら PDF 生成のエンジンに latex 使うのはやめたほうがいいかも

スタイルが Word みたいのでいいなら docx を使えばいいのかも
pandoc は docx への変換もサポートしてるし そこから PDF 化できる
試してみると docx はそれなりにはいい感じ
--reference-doc でテンプレートの docx を指定するとそのスタイルを使ってくれる
だけどこれまで使ってた Word ファイルをそのまま指定だと スタイルが完全に思い通りにあたってないことも
内部的に使うスタイル名が決まってるみたいなので pandoc でデフォルトテンプレートの docx を出力して その docx のスタイルをカスタマイズして --reference-doc に指定するのが良いみたい

テキストベースなものと違ってコピペで済ませられないのでスタイル設定し直すのは結構面倒
それに本文・見出し・箇条書き・表くらいなシンプルなドキュメントなら十分そうだけど 表紙ページとかインデックスページとかヘッダ・フッタとか複数セクションの分割とかってどうすればいいんだろう?

docx 変換は情報が少なく苦労も多そう
やっぱり HTML を通した PDF 変換が一番無難でカスタマイズしやすいのかも
中間ファイルが HTML なら HTML をパースして構造置き換えたりも簡単にできるし スタイル調整も CSS で楽にできるし



HTML を使う場合は デフォルトエンジンは wkhtmltopdf
懐かしい というかまだ生きてたんだ
公式ページを見てみると QtWebkit の開発が停止したり復活したり Chrome の Blink 化や PhantomJS の終了だったり 色々あったみたい
https://wkhtmltopdf.org/status.html
プロジェクトは続いてるけど あまり活発に開発されてるわけでもないようで 別の類似ツールの使用も検討してくださいって言ってるくらいだし 最近は Chrome のヘッドレスモードで PDF できるから使わなくてもいい気はしてる

一応入れてみる
pandoc のエンジンとしては対応してるけど同梱されてないし Docker イメージに wkhtmltopdf を含んでるのもなかったので ubuntu を使った pandoc イメージで apt を使ってインストール
Qt 関係が依存で必要だからか GB 単位でインストールが必要で結構重い
これだけだと日本語が□になってたので日本語フォントも追加でインストールが必要
wkhtmltopdf のオプションに詳しくないので全体的な調整がやりづらいし wkhtmltopdf じゃなくて Chrome で PDF 化でいいかな



とりあえず pandoc で HTML 化して Chrome で PDF 化する方向にしようとしたけど ブラウザの HTML 印刷機能は弱かった
そもそもウェブページの印刷用だから ドキュメントの作成レベルのクオリティは要らないはずといえばそうなんだけど

ヘッダ・フッタをうまく扱えない
CSS の定義上は各コーナーを扱うプロパティは存在するけど未だにブラウザはサポートしてないらしい
position に absolute, fixed を使う方法はブラウザ依存だし Chrome だと各ページに表示はできるけど カウンターが更新されない
全てのページに同じページ数が表示される
全ページ数を表示する方法もない
JavaScript でコンテンツサイズとページサイズを計算してページ分けして それぞれのページに別々のヘッダ・フッタの要素を配置するという無理やりなものもあったけど画像や表とかがページを挟むとかもあるとどこかで問題出そう

思ったより良かった部分はセクション分け機能は動いて

@page foo {
size: A4 portrait;
}
@page bar {
size: A4 landscape;
}

.section1 {
page: foo;
}
.section2 {
page: bar;
}

というスタイルを当てれば section1 と section2 でページが分かれて セクションごとに用紙の縦横を切り替えられる
ヘッダ・フッタのコーナーがサポートされれば @page のブロックに配置することでセクションごとのヘッダ・フッタも作れるはず
ただここも完璧ではなくて セクションごとに用紙の縦横は切り替わったけど内部の計算は用紙サイズの変更を反映してないみたい

1 つめのセクションは用紙が縦で 2 つめのセクションは用紙が横として それぞれの本文は同じで数字付きの箇条書き

1. aaa
2. aaa
3. aaa
4. aaa
...

みたいなの

数字は 1 ~ 20 までで

縦:
1 枚目 → 1 ~ 15
2 枚目 → 16 ~ 20
横:
1 枚目 → 1 ~ 8
2 枚目 → 9 ~ 16
3 枚目 → 17 ~ 20

を期待したのに横でも縦と同じ 2 枚になって 1 枚目に 15 まで入って 2 枚目が 16 からになってる
縮小されて全部が見えるならまだいいけど 8 までしか入ってなくて 9 から 15 は用紙の範囲外でプレビューにも映ってない
実際には使い物にならない

こういう問題があるし HTML はドキュメントの PDF 作成には向いてなさそう
latex でスタイルカスタマイズを頑張るか pandoc の PDF 化エンジンで選択できて wkhtmltopdf でも推奨してた weasyprint を試してみるとかかなー
JSX 中で useMemo をしたい
React で input イベントごとにレンダリングしてると 入力が遅く感じる時がある
大きめの配列でそれぞれの処理も複雑なときにありがち

単純に要素ごとに 100ms 待機させた例

const Component = ({ items }) => {
const [text, setText] = useState("")
const onChange = (event) => setText(event.target.value)

return (
<div>
<input value={text} onChange={onChange} />
<p>{text}</p>
<ul>
{items.map((item, index) => {
const d = Date.now()
while (Date.now() < d + 100) { }
return (
<li key={index}>{item.name}</li>
)
})}
</ul>
</div>
)
}

これで文字を打つと遅く感じる
items が変わらなければ ul の中はずっと同じなんだから useMemo でメモしておけばいい
理想的な書き方は ul の中に直接書いてこういう感じ

const Component = ({ items }) => {
const [text, setText] = useState("")
const onChange = (event) => setText(event.target.value)

return (
<div>
<input value={text} onChange={onChange} />
<p>{text}</p>
<ul>
{useMemo(() => {
return items.map((item, index) => {
const d = Date.now()
while (Date.now() < d + 100) { }
return (
<li key={index}>{item.name}</li>
)
})
}, [items])}
</ul>
</div>
)
}

この場合はこれでも動くけどフックは関数の最初にまとめておきたい
フックは呼び出し順の違いや呼び出されない場合があったらダメなど特殊なルールがあるので 普通の関数みたいに自由に使うと思わぬ問題が出てきそうで それのために使うたびにここは大丈夫かみたいなことを慎重に確認とかしたくないし
でもそうなると ul の中だけが先に来てコンポーネント定義が読みづらくなる
先にフックの use だけ書いておいて JSX 中にメモしたい内容をかけるようなフックを useRef を使って自作

const useCache = () => {
const ref = useRef()
return (...a) => {
const fn = a.pop()
if (ref.current &&
ref.current.deps.length === a.length &&
ref.current.deps.every((e, i) => e === a[i])
) {
return ref.current.cache
}
const next = fn(...a)
ref.current = { deps: a, cache: next }
return next
}
}

const Component = ({ items }) => {
const [text, setText] = useState("")
const onChange = (event) => setText(event.target.value)
const cache = useCache()

return (
<div>
<input value={text} onChange={onChange} />
<p>{text}</p>
<ul>
{cache(items, () => {
return items.map((item, index) => {
const d = Date.now()
while (Date.now() < d + 100) { }
return (
<li key={index}>{item.name}</li>
)
})
})}
</ul>
</div>
)
}

見やすくかけるようになった
インデントの反対は?
アウトデントという単語があるようです
https://eow.alc.co.jp/search?q=outdent

in の反対なので out と言われれば自然です

ただ そういう処理では dedent というワードしかみたことがなかったので こっちのほうがしっくりきます
語感的にも ででんと のほうがなんか面白い感じがしますし

Python だと標準ライブラリのメソッド名が dedent です
https://docs.python.org/ja/3/library/textwrap.html#textwrap.dedent

JavaScript は標準ではないですが npm で週間 800 万ダウンロードされてるライブラリが dedent です
https://www.npmjs.com/package/dedent

まだ stage1 ですが TC39 に提案されている機能で String.dedent というのもあります

プログラミング関係では dedent ということでしょうか
英語での議論が辛い
英語で聞いてみたりしたときに シンプルに求めてる回答が来れば お礼だけ返して楽に終わるけど ときどき面倒なことになるときがある
こっちが求めてるところ以外で謎の指摘が来たり
日本語なら軽く 「これはこういうものだからそれで問題ない むしろこのほうが優れてる」 と答えて終わりだけど英語だとそれだけでも面倒
複雑な文になると翻訳ツール使ってもうまく伝わらないような形になることもあるし 言い回しの調整とかで時間取られる

返信したらしたで また返答来たりで長く議論が続きそうな予感しかない
疲れるしそれに勝って得られるものもないからもうそれでいいよとそのままスルーすることが多いけど 日本語でなら絶対にこれはこうあるべきと言いたいくらいなところだからなんだかなーという気分になる
forEach がまた使われるかも
昔の JavaScript は配列の foreach 機能がなくて for 文でループカウンタをインクリメントするレガシー感ある方法でした
一応 for-in はありましたが デメリットも多く配列ではループカウンタのほうが主流だったと思います

その後 ES5 が出て forEach メソッドが使えるとかなり便利になったのであちこちで forEach が使われるようになりました

そして ES2015 からは for-of 文ができたので forEach は廃れて for-of が主流になりました
forEach は関数実行なので return か throw しか終わる方法がないですが for-of は制御構文なので break や continue も使えます
さらに 配列っぽいオブジェクトもサポートしていて forEach を使うメリットはあまり無くなりました
forEach は関数を渡すので便利なところでは便利ですが インデックスや配列自体も引数に渡されることで思い通りに動かなかったり 配列のそれぞれの要素のメソッドを呼び出したいなどで 単に関数を渡すだけにはならずアロー関数を書くことがほとんどで for-of より優れてるケースは稀だと思います

そんな感じで アプリケーション中に 1 つも forEach が存在しないことも増えてきたのですが 最近ではまた使うケースが増えてきました
その理由は ?. です

null または配列が入ってる変数をループ処理したくて null なら何も実行しなくて良いです
そういうときに for-of だと null は iterable ではないので null なら空配列とするか その前に if 文で分岐が必要です

for (const item of getItems() ?? []) {
console.log(item)
}

// or

const items = getItems()
if (items) {
for (const item of items) {
console.log(item)
}
}

前者の方はそこまで長くないですし これでも良いのですが forEach だとこうできます

getItems()?.forEach(item => console.log(item))

書きやすくシンプルです
内部で break や continue を使い分けたいなら 多少面倒でも for-of のほうが良いですが こういう短く forEach でも困らない処理なら forEach のほうが良さそうです
データベースでタイムスタンプの一致が検索できない
RDB のクエリで WHERE にタイムスタンプ型の列を対象にするとなぜか毎回何も一致しません
レコードが存在するのは確認していて SELECT で取得したデータをそのまま WHERE に入れても一致しません

これに結構苦戦させられたのですが 単純に精度の問題でした
環境は Node.js なのでタイムスタンプ型は Date 型として取得できます
node-pg でもそれをラップした knex 等でも同じです

Date 型はミリ秒までですが PostgreSQL で timestamp と指定したときの精度はマイクロ秒まであります
なので Node.js で受け取った段階でマイクロ秒は切り捨てられて その値で検索しても一致はしないわけです
大きな数値の ID などで数値型でも有り得そうな問題ですが 受け取ったタイムスタンプが見た目上問題なさそうなので原因特定に時間がかかりました

言語の都合でデータベースの定義を変更するのはどうかなという気はするものの その Node.js のアプリ専用なデータベースですし 実際にはミリ秒以下は必要になることはないので秒までにすることで対処しました
タイムスタンプ型のオプションで精度は変更できます
また CURRENT_TIMESTAMP などを使わず Node.js 側で現在時刻を取得して保存すればマイクロ秒は常に 0 になるので定義を変更せずに対処することもできます

もしマイクロ秒まで必要となると select 時にキャストして文字列型で取得するしかなさそうです

pg("table")
.select("id", pg.raw("xxx_timestamp::text"))
レコードの新規作成と更新は全部まとめたらいい気がした
クライアント画面では サーバからデータを受け取ればそれを初期値として 受け取らないなら新規作成として画面を作る
保存ボタン押したときは メソッドやパスは同じところに POST する
サーバ側では SQL の insert を使って on conflict do update にすることですでにある ID なら更新にする

Node.js の knex だとこういう感じで書くだけだからかんたん

knex("table1").insert(user_data).onConflict("id").merge()

ID を自動で連番にしたいなら 列定義で連番にするようにしておいて 新規作成の場合には 「id: undefined」 となるようにすれば DEFAULT 扱いになって連番で insert できる
これで新規作成と更新で処理を分ける必要がなくなってけっこう楽

on conflict do update はレコードがあるのはわかっていて 複数のレコードをまとめて更新したいというときにも使えてかなり便利
React の state をミュータブルにする
React の state ってイミュータブルが前提になってます
深い部分のプロパティを変えるのが面倒です
state.foo.bar.baz[1] をインクリメントするだけでこうです

setState({
...state,
foo: {
...state.foo,
bar: {
...state.foo.bar,
baz: state.foo.bar.baz.map((x, i) => i === 1 ? x + 1 : x)
}
}
})

オブジェクトのプロパティを書き換えたらその情報をもとにイミュータブルなまま新しいオブジェクトを作ってくれるライブラリもありますが これだけのためにライブラリ入れるのもなという気持ちがあります
単純にこういうフックを使えば良い気がしました

const useMutableState = (init) => {
const [state, setState] = useState({ value: init })
const updateState = useCallback((update) => {
update(state.value)
setState({ value: state.value })
})
return [state.value, updateState]
}

useState の setState 関数は引数として受け取る値が同じならレンダリングをスキップします
オブジェクトのプロパティ変更だと同じオブジェクト扱いでレンダリングされません
それを回避するために setState に渡すオブジェクトの参照だけを毎回変えています

setState の不要なレンダリングを避けてくれるメリットを捨てていますが 普段の setState でも毎回新しいオブジェクトを渡していて毎回レンダリングされます

setState({ ...state, [prop]: value })

それならこれでもいいと思うんです
このフックの使い方はこういう感じで プロパティを直接変更します

const Component = () => {
const [state, updateState] = useMutableState(
{foo: {bar: {baz: [0, 0]}}}
)

const onClick = () => {
updateState(s => {
s.foo.bar.baz[1]++
})
}

return (
<button onClick={onClick}>{state.foo.bar.baz[1]}</button>
)
}

少し使っただけでは問題なさそうだったのですが 実際の使用になると問題がありました
state.foo をコンポーネントの props やフックの依存配列に渡すときです
foo の中に変更があったのに foo が同じオブジェクトなので変更を検知できません

複雑なオブジェクトを state に持つ場合って部分的なプロパティを props や依存配列に渡すことが多いので 実用はできなさそうです
やっぱり面倒なコードを書くかライブラリだよりになりそうです
JavaScript でグローバルスコープへのアクセスを完全に禁止できる?
ページ上で JavaScript コードを書いて実行できる環境で window や location などにアクセスできなくしたいとき
window などは上書きできないのでローカルスコープに同名変数を作って そのスコープでコードを実行する

const exec = (source) => {
const window = {}
const location = null
const self = null

eval(source)
}

exec(`
console.log(location)
console.log(window.location)
`)
// null
// undefined

document などから辿れるのでそれらも同じように隠す

これだと this から window を参照できる

exec(`
console.log((function() { return this })().location)
`)

use strict をつけて this が window にならないようにする

const exec = (source) => {
"use strict"
const window = {}
const location = null
const self = null

eval(source)
}

これでも Function 関数を使えばグローバルスコープの関数を作れるので location などにアクセスできる

exec(`
Function("console.log(location)")()
`)

単純に Function を null にしても リテラルから関数を作れる

const exec = (source) => {
"use strict"
const window = {}
const location = null
const self = null
const Function = null

eval(source)
}

exec(`
(function() {}).constructor("console.log(location)")()
`)

Function.prototype.constructor も隠す

Function.prototype.constructor = null
const exec = (source) => {
"use strict"
const window = {}
const location = null
const self = null
const Function = null

eval(source)
}

ジェネレータや async 関数も同じようにする
Worker のスコープでの実行も避けるので Worker も隠す

思いつく方法は使えなくできたけど これで完全?
クライアントサイド検索ありかな
リストや表の表示のコンポーネントを見てると 全件データがある前提で JavaScript 内でフィルタやソートやページングしてるのをみることがけっこうある
検索ページで検索条件やソート・ページが切り替わるごとにサーバにリクエストしてリストを表示してるとこれらの機能を役立たせられないし 表示せずデータ受信だけならそんなに時間かからないし 画面開いたときに全件取得して保持して JavaScript で検索やソートでいいかな
新規に頻繁にデータが来て直ぐに反映したいわけじゃなければ 更新は 1 時間に 1 回とかでもいいし
必要ならユーザが更新ボタン押せば再取得とかで困らなそう

全件が何百万件ってデータがあるならともかく数千件レベルなら問題なさそうだし
サーバ側も基本全員に同じデータだから変数上に保持しておいたのをそのまま返すだけでデータベース経由しなくていいし
ありな気がしてきた
Chrome 92
Chrome 92 がリリースされた

新しいタブにファビコンがついた
Chrome アイコンの影みたいなの


開発者向け機能だと JavaScript に .at() や crypto.randomUUID() が増えた

"foo".at(-1)
// "o"

[1,2].at(-1)
// 2

crypto.randomUUID()
// "90b7eb58-6640-4ad4-a546-532783d17513"
メソッドをstatic 関数化
const handler = (ctx, name) => (...args) => ctx.prototype[name].call(...args)
const S = ctor => new Proxy(ctor, { get: handler })

使用例

S(Array).reverse([1, 2, 3])
// [3, 2, 1]

S(String).slice("foobar", 2, 4)
// ob

パイプライン演算子が来たりでもないと基本はメソッドで良さそう
数少ない役立つところは map などの関数に入れる使い方

const SString = S(String)
const texts = [" a ", " b"].map(SString.trim)
// ["a", "b"]

毎回 「x => x.trim()」 って書くの疲れるし
文字数的にはそんなにだけど記号打つのが面倒

この方法だと prototype のメソッドを call するのと一緒なのでキャストされる
配列風なものを配列として扱いたいとかでは使えるけど 変換いらずにただその名前のメソッドを呼び出せればいいならこれで十分そう

const handler = (ctx, name) => (value, ...args) => value[name](...args)
const X = new Proxy({}, { get: handler })

const texts = [" a ", " b"].map(X.trim)
// ["a", "b"]

X.trim が引数の trim を呼び出す関数になるなら
parseInt(X, 2) で X のところに引数が入って呼び出される関数にもなってほしい
parseInt そのままではただの関数呼び出しに X が渡されるだけなので一工夫する

const handler = (ctx, name) => (value, ...args) => value[name](...args)
const fn = f => (...args) => a => f(...args.map(x => x === X ? a : x))
const X = new Proxy(fn, { get: handler })

X に関数を渡して その返り値を関数として使う

const hex2dec = X(parseInt)(X, 16)
hex2dec("ff")
// 255

const bit = X(Math.pow)(2, X)
bit(16)
// 65536

見やすさはなんかいまいち
やっぱり構文として対応してほしい
けど PHP では提案があったけど複雑すぎるとかで却下された話を聞いたし JavaScript でも入らなそうな気はする

少し長くなるけどアロー関数でできるといえばできるし 関数本体部分でちゃんと呼び出し形式になっていてどう実行されるかがわかりやすいし

X => parseInt(X, 2)
X => X.trim()
更新条件に更新日時を入れてもトランザクションが被ると上書きされる?
RDB をたまにしか使ってないとロックみたいな複雑なところは こういうときにどうなるんだっけ?と思って毎回調べてたりするのでメモ代わりに

UPDATE 時の WHERE で更新日時が等しいことを条件としていれば上書きされることはないはず と思ってたけど トランザクションが同時実行された場合は

1: トランザクション A が書き換えようとする
  条件に一致するので正常に書き換えできる

2: トランザクション B が書き換えようとする
  A がコミットされてないので古いバージョンが見えている
  条件に一致するので正常に書き換えできる

3: トランザクション A をコミットする
4: トランザクション B をコミットする
5: A の書き換えが消える

になるような気がする
これが困るなら同時実行されないように分離レベルとか設定必要なんだっけ?

試したほうが早いと思ってやってみると PostgreSQL のデフォルトだと特に問題なかった

begin;
select * from tt;

id | v | updated_at
----+---+---------------------
1 | 1 | 2021-07-01 09:00:00
(1 row)

update tt set
v = 2,
updated_at = '2021-07-01 10:00:00'
where id = 1
and updated_at = '2021-07-01 09:00:00';

select * from tt;

id | v | updated_at
----+---+---------------------
1 | 2 | 2021-07-01 10:00:00
(1 row)
begin;
select * from tt;

id | v | updated_at
----+---+---------------------
1 | 1 | 2021-07-01 09:00:00
(1 row)

update tt set
v = 3,
updated_at = '2021-07-01 10:00:10'
where id = 1
and updated_at = '2021-07-01 09:00:00';

-- 待機状態
commit;
select * from tt;

id | v | updated_at
----+---+---------------------
1 | 2 | 2021-07-01 10:00:00
(1 row)
-- コミットされたので update の処理が終わる

select * from tt;
id | v | updated_at
----+---+---------------------
1 | 2 | 2021-07-01 10:00:00
(1 row)

-- 更新されてない

B 側の UPDATE 時にロックされてて UPDATE が処理されず A 側がコミットされてから処理される
その結果 更新日時が違うので B の更新はされてない

余計な心配だったみたい
Promise を分割代入しようとしたときのエラーがわかりづらい
const [a, b] = 1
const [a, b] = null
const [a, b] = {}

こういう処理をしようとすると

1 is not iterable
null is not iterable
{} is not iterable

のようなエラーが出ます

not iterable と言われるので分割代入のところに入れようとしている値がおかしいとわかります
ですがここの右辺が Promise の場合は

const [a, b] = Promise.resolve(1)
undefined is not a function

undefined なんてどこにもないので何を言ってるかわからないエラーです
Promise が右辺になるのは await 忘れでよくあることなのにこのわかりづらさは不便です
ここまでシンプルな行ならともかく 右辺がもっと複雑だとどの部分が悪いのかわからずに困ります

とりあえず 配列の分割代入の行で undefined is not a function と言われたら await 漏れを確認するのがよさそうです
Promise.all はそのまま引数に渡して使えない
これを実行するとエラーでした

const promises = [
[Promise.resolve(1), Promise.resolve(2)],
[Promise.resolve(3), Promise.resolve(4)],
]

promises.map(Promise.all)

Chrome では
「Promise.all called on non-object」

Firefox では
「Receiver of Promise.all call is not a non-null object」

Promise.all を関数として渡しているので内部的に呼び出されるときに Promise というオブジェクトのメソッドというコンテキストで呼び出されないからですが この制限があると

promises.map(x => Promise.all(x))

のように呼び出さないといけなくなって面倒なんですよね

昔は console.log も同じ問題があったのですが 結構前に解決されて

fetch("").then(x => x.text()).then(console.log)

みたいに then に直接渡すだけでも良くなりました

組み込み関数なら全てこういう風にしてほしいものです
this の考え方が関数渡しに適してないので this は消えてほしいです

自動で this を bind 済みにしてくれる記法ができればいいんですけどね

obj..method



obj.method.bind(obj)

と同じになるみたいな
自由にパスワード設定させてくれないならシステムが自動で発行してほしい
パスワードは完全に自由に設定できるべきだと思います
ユーザが a というパスワードでいいならシステムはそれも受け入れるべきです
それでアカウント乗っ取られても自己責任でそれでいいんです

なのに
「大文字小文字数字を混ぜてください」
「記号を入れてください」
「使える記号はこれだけです」
「文字数は何文字から何文字に収めてください」
「何ヶ月に一回パスワードを変更してください」
「同じパスワードは使いまわせません」

こんな注文がいっぱい要求されることが多いです
ほんとうんざりです
警告として強度が弱いですよ と言われるくらいで無視できるならいいのに強制です

強制したところで簡単なパスワードにしたい人は結局 「passwordA1!」 みたいに最後に大文字数字記号の基本的なのをつけるだけで意味ないと思うんですよね

ここまで要求するならもうシステム側でパスワードを自動生成して「これを使ってください」といえばいいと思うんです
あれがだめこれがだめと言われながらパスワードを考える時間が無駄です
それに システムの自動生成なら使い回し問題もありえないですし
Fedora34 で cifs マウントできない件 もうすぐ修正されそう
https://bodhi.fedoraproject.org/updates/FEDORA-2021-6679746a3d

testing ステータスになってる

Fedora のパッケージのリリースやアップデート情報はこのサイトで見れるみたい
https://bodhi.fedoraproject.org/

ドメインにある bodhi ってなにかと思って調べてたら Fedora のアップデートを管理するウェブシステムの名前でリポジトリもあった
https://github.com/fedora-infra/bodhi



上のは cifs-utils と同じ原因でエラーになってる別パッケージで cifs-utils がこのパッケージを使ってるわけじゃないので cifs-utils の修正とは関係なかった
cifs-utils の方はフォーラムではメンテナの人の個人的な事情でしばらく修正のリリースができないみたいだし まだ修正は先そう
動的ページのインデックスの問題点?
クエリパラメータ中のユーザ入力を画面内に表示するページを作ったら ユーザ入力ありで Google にインデックスされてた
なんでこんな全然関係なさそうな本文のページが自サイト内の検索結果に出てくるんだろうと思ってたらそういうことだったのか
そのページを開いたらちゃんとその誰かが入力したワードがクエリパラメータに含まれていて その内容がページ内に表示されるし 検索結果としては間違ってはないと思う

例えば 「foo bar」 という文章を打ったとして クエリパラメータは base64 形式にして 「usertext=Zm9vIGJhcg」 となるとする
Google 検索結果の候補に 「foo bar」 という内容のページがあって リンク先が 「/page.html?usertext=Zm9vIGJhcg」 という感じ
ブログみたいな投稿するサービスでもなくサーバサイド処理は一切なしの静的ファイルをおいてるだけのサイトなので 文章を書いた人は検索エンジンに載るとは思わないだろうしプライベートな内容を書いてると困りそう
でも自分でそのページを開いただけだとインデックスされないだろうし 自分でリンクをどこかのウェブページに貼ったのだろうから別にいいのかな

どっちかというと 多くの人が使うとサイト内検索のノイズが増えてサイト管理者の方が困るのかも
JavaScript の処理後の結果をインデックスしてくれるのはメリットだけどこういう問題もあったとは
App History API
Chrome の開発中の機能を見てるとこんなのがあった
https://wicg.github.io/app-history/

window.appHistory が追加されるみたい
window.history よりも SPA ページを作りやすくするものだとか
過去の履歴を取得できたり navigate メソッドでページ遷移できる
AppHistory 自体が EventTarget を継承していて navigate などのイベントを受け取れる

現状の history は history といいつつ過去の状態は見れないし pushState と replaceState で URL 書き換えるくらい
戻る/進むのイベントは window の popstate イベントだからページの更新処理は pushState や replaceState を呼び出したところと popstate イベントの複数箇所書かないといけなくて不便だった
このあたりが解決するのはよさそう
Vivaldi でタブが消せないバグ
ときどき Vivaldi でタブが消せなくなるなと思っていたら原因がわかりました
devtools 上で F5 キーを押してページをリロードすると タブが消せなくなります
タブエリアでミドルクリックしたりタブの×ボタンを押したり右クリックからタブを閉じるを選んでも反応なしです

今のところ Vivaldi を再起動する以外にタブを消せるようになる方法が見つかってません
最新版にアップデートしても修正されてないようです

以前は devtools 上で F5 キーを押してもページがロードされなくて不便だったのが解消されたと喜んでいたらこれです
どうしてもこうも Vivaldi は devtools 周りのバグが多いのでしょうね



書いた直後にもしやと思って試すと解決策が見つかりました

devtools を再度開いて console で 「close()」 を実行します
Vivaldi の UI 上からでは閉じれませんが JavaScript からだと閉じれました
一度閉じるとそれ以降は Vivaldi の UI 上からタブを閉じれるようになります
Windows 10 が終わるみたい
Windows 10 が最後の Windows だって言ってたのに
最近言われている 11 という噂も全く信用してなかったのに まさか本当だったなんて

11 という名前は発表されてないので別の名前かもしれませんが Windows 10 のメジャーアップデートごとのサポート期限のページを見てみると Home / Pro / Enterprise / Education 全てに提供終了日が 2025/10/14 と書かれています
https://docs.microsoft.com/ja-jp/lifecycle/products/windows-10-home-and-pro
https://docs.microsoft.com/ja-jp/lifecycle/products/windows-10-enterprise-and-education

急な発表なのに 2025 年って早すぎ
あとたったの 4 年しかないです
次のが来るということがわかっていて そろそろ来そうという時期なら これくらいの移行期間でも良いと思います
ですが Windows10 はこれが最後と言われていて サポート終了が来るなんて想定していなかったわけですから 影響的には出たばかりに 4 年後に終了と発表されるのと大差ないです

ただ 「提供終了日」 とあり それまでは半期チャンネルでリリースすると書かれているので 単にメジャーアップデートが出なくなるだけなのかもしれません
Windows7 の延長などを見ても セキュリティフィックスとかも提供されなくなるのはもっと先の気がします

10 ではなくなるというのだと やっぱりこれまでのように OS を買い直すことになるのでしょうか
自動で無料でのアップグレードならいいですが そうじゃなければ Windows7 と同じ未来しか見えません
ちなみに自宅は未だに Windows7 が現役です
バグか仕様かは主観的なものだと思う
これはバグだ仕様だの話を見るときによく思うこと
製作者から見て仕様であっても 使う側からみるとバグ なんてのはよくあるもの
どっちが正しいと言うのはないと思うし どっちも正解だと思う
使う側で使えない以上 その人からしてはバグなんだし
もちろん CSV パーサで JSON がパースできない みたいに明らかにおかしいのもあるけど

機能的に一貫性があればこういうときはこうなるはずだ これとこれを組み合わせたらこういう事ができるはずだ というところで 想定したとおりに動かないみたいもの
作る側が想定していなかったから動かないし直すつもりはないというのは別に構わないと思う
でもそれを仕様と呼んだところで ユーザからすれば動くべきところが動かないというだけ
製作者側が一方的にこれは仕様かバグかを決めるならその違いに対して意味はないと思う
仕様かバグかはどっちでも良くて 動くか動かないか 使えるか使えないか だし

仕様書なんてのがある世界で生きてる人は それが一つの基準かもしれない
でもそれはすべてのケースが網羅されてるの?
そこに書いてない挙動は何があっても全部仕様?
そう契約してるならそれでいいと思うけど すべてがそういうわけじゃないよね

作るときに想定してなくても 「言われてみると そのほうが正しい」 と思うなら直すこともあるし 逆にバグと認めていても直さない・直せないことだってある
バグ報告があって それが仕様かバグかの判定なんていらないから 直すか直さないか で伝えたらいいと思う
実際に どう見てもバグであっても自分の方が正しいと仕様と言い切ったり 間違ってると理解していても直せないから仕様ということにする人だっている
仕様です って返答されても 「だから使えないって言ってるんだって」 としかならないしイライラさせるだけ
まだ 直さない・直せないと言う答えのほうが納得できると思う

あと 自分の周りというかネットも含めて見かける範囲では 昔ながらの IT 系で働いてる人に 過剰と思うほど仕様かバグかをはっきりさせたがる人が多いように思う
バグと言う言葉に過敏に反応して バグ扱いされたら これはバグじゃない!とすごく熱くなってる
バグと言う言葉自体を「言ってはいけない言葉」であるかのように扱ってるようにも見える
業界的なことなのかもしれないけど 自分はユーザ寄りで気軽にバグバグ言うので 理解できない世界
自分が作ったプログラムを我が子のように思ってたりするのかな?
バグ扱い=家族を侮辱されたと考えれば あの必死さもわからなくもない……ような気もするような
mime type いらないと思う
ウェブでは Content-type に mime type が使われます
これを見て JavaScript を実行するかなどの判断が行われるので 全部 text/html 扱いの簡易サーバだと動かない部分があります
簡易サーバを自作するときでもこの当たりが面倒なので パッケージに頼ることが多いです

この mime type ですがいらないと思うんです
ちゃんとファイルの中身を見た上で ファイルの種類を判断してサーバがレスポンスを返してるならともかく ほとんどの場合は拡張子から自動変換するだけです
ファイルの中身を見てからファイルの種類を判断して mime type を返すようなものは見たことがないです

Node.js だと Express や Koa などのフレームワークはだいたいこのどちらかのパッケージを使っています
mime type と拡張子の対応が記述されている JavaScript オブジェクト形式のデータベースファイルです
https://github.com/broofa/mime/blob/master/types/standard.js
https://github.com/jshttp/mime-db/blob/master/db.json

どちらも週間ダウンロード数が 3000 万を超えています
外部パッケージは使わない方針の hapi でもこれを使っています

Python でも標準の mimetypes.guess_type で mime type を取得できます

都度 ファイルの中身を確認するのは処理が遅くなりますし ファイルごとに mime type 情報を別に保持しておくのも面倒が多いので仕方ないとは思います
ですが それなら最初から mime type じゃなくて拡張子を見ればいいと思うんです

ウェブページの URL なので HTML ファイルに .html がついてないとか .php を HTML ファイルとしたいとかあるのはわかります
そういうときだけ HTTP ヘッダーのレスポンスに情報を付加して なにもないデフォルト値は拡張子を見てほしいです

今更 変わらないとは思いますけど ちょっとしたことするだけでも mime type 変換もいるのは面倒なんですよね
ブラウザ拡張機能として localhost サーバから未指定・text/plain・text/html が来たときは拡張子から判断したものに置き換えるようにするのはありかもですね
サーバは色々変わってもブラウザは同じですし
VSCode でタブキーを押すとフォーカスが移動する
タブ文字を入力したくてタブキーを押してるのに入力されず 代わりにフォーカスが別のところに移動します
ブラウザの標準の textarea でタブを打つと次の input に移動してしまうみたいな状況です

以前もこの状態になったことがあったのですが 直し方がわからず VSCode 全体を再起動することで直ったこともあってバグかなと思ってました
今回 その原因がわかりました

ちゃんとした タブ入力でフォーカス切り替えを行うモードでした
このモードが有効になっていると右下の現在のカーソル位置などが書いているところに「Tab Moves Focus」と表示されます

そして Ctrl-M キーがこのモードを切り替えるショートカットキーになっていました
Ctrl-N のつもりで間違って押していたのでしょう

知らないうちにモードが切り替わってしまうと直し方がわからず困りますね
Vivaldi 4 で翻訳機能が使えるようになった
https://vivaldi.com/ja/whatsnew/desktopnews/vivaldi-4-0/
新機能で開いてるページを翻訳できるようになりました
翻訳機能の無さが Chrome に比べて不便なところでしたが とうとう実装されたようです

ただ 使ってみた感じでは翻訳の質は Google 翻訳に劣ります
Lingvanex というサービスを Vivaldi がホストして使ってるそうです
翻訳の質が高いと評判の DeepL とか使ってくれたらいいのに

翻訳の方法は URL バーのアイコンからです
Chrome とは違って右クリックメニューから翻訳できません
アイコンは常にあるわけではなく 自動判断で翻訳が必要そうなら出てくるようです
ページを開いても出てこないこともあれば 遅れて出てくることもあります
このあたりは改善していってほしいですね
Fedora 34 で cifs マウントできなくなった
Fedora 34 に更新して以降 Windows の共有フォルダのマウントができていません
ログを見ると エラーが出ています

libcap-ng used by "/usr/sbin/mount.cifs" failed due to not having CAP_SETPCAP in capng_apply

すでに Redhat の Bugzilla にはバグ報告があります

https://bugzilla.redhat.com/show_bug.cgi?id=1924218
https://bugzilla.redhat.com/show_bug.cgi?id=1962920

自分で最新版をビルドして解決したという報告もあるように cifs-utils 本体側では対応済みのようです
https://lists.samba.org/archive/samba-technical/2020-December/136156.html

去年末リリースの 6.12 の修正内容に CAP_SETPCAP 関係がいくつかあります
積極的にパッケージの更新を行う Fedora なら 6.12 をリリースしてくれても良いと思うのですが まだ 6.11 です

自分でビルドするのも面倒なので早く dnf upgrade で更新できるようになってほしいですね
Windows10 のユーザ作成で秘密の質問を要求される
直前の記事の通り マイクロソフトアカウントで Windows ユーザを作ると問題が出たので最初からオフラインアカウントで作り直すことにしました
相変わらず オフラインアカウントは隠し機能みたいになってるなぁ と思いながらパスワードを登録したところで 秘密の質問を登録してくださいという画面が出ました
これまでは出たことなかったですし マイクロソフトアカウントを使う場合は出ませんでした
最近の Windows ではオフラインアカウント作成時に必須になってるみたいです
スキップもできませんでした

秘密の質問を強制されるサービスがありますが すごくやめてほしいです
たいていの項目は家族や友人なら答えれる可能性があるようなものですし ターゲットが誰かを特定できれば情報は入手しやすいものです
普通のパスワードより不正ログインの難易度が落ちてます

質問内容と全く関係ない回答もできますが それはそれで覚えられないです
3 個設定が必要で 全部に同じ答えが使えなかったり 使える文字が ひらがなのみ みたいな制限もあって 使いまわしづらいですし

せめて質問自体が自由入力ならまだ有用だと思うのですけど

仮のもので登録してセットアップ処理の完了後に消そうと思って キーボードを適当にカチャカチャと押した文字列で登録したのに 設定やコントロールパネルのユーザ画面では秘密の質問の設定が見当たらなかったです
自分でもわからないくらいに適当な文字列で設定したので 悪用されることは考えにくいですし このまま放置です
Microsoft アカウントで Windows のユーザを作ったときのユーザフォルダの名前
C:\Users\【ここ】

↑の部分に入る名前は基本はユーザ名がそのままです
ですが 初回セットアップ時にマイクロソフトアカウントを使ってみたら メールアドレスの最初の 5 文字になっていました

中途半端なところですし 気持ち悪いのでローカルアカウントに切り替えました
切り替えのときにユーザ名を設定できたので ユーザフォルダ名も切り替わるものだと思ってたのですが ユーザフォルダ名はそのままでした

ただでさえ 謎の 5 文字で気持ち悪いのに ログイン時などに使うユーザ名とユーザフォルダ名が違うというさらに気持ち悪い状態になってしまいました
PHP で指定の名前空間に属する関数やクラスなどの一覧を取得
指定の名前空間内で定義されているものの一覧を確認したい
だけど そういう関数はないみたいだったので自作
すべての関数取得やすべてのクラス取得といった関数はあったので 全部取得してから名前空間でフィルタしてる
サブ名前空間も含めるかを指定可能

function get_namespace_items($namespace, $include_sub_namespaces = false) {
$namespace = trim($namespace, '\\') . '\\';

$classes = get_declared_classes();
$interfaces = get_declared_interfaces();
$traits = get_declared_traits();
$functions = get_defined_functions();
$constants = get_defined_constants();

$filter = function ($items) use ($namespace, $include_sub_namespaces) {
$matched = [];
foreach ($items as $item) {
if (strpos($item, $namespace) === 0) {
if (
$include_sub_namespaces ||
strpos(substr($item, strlen($namespace)), '\\') === false
) {
$matched[] = $item;
}
}
}
return $matched;
};

return [
'class' => $filter($classes),
'interface' => $filter($interfaces),
'trait' => $filter($traits),
'function' => $filter($functions['user']),
'constant' => $filter(array_keys($constants)),
];
}



使用例

<?php

function get_namespace_items($namespace, $include_sub_namespaces = false) {
$namespace = trim($namespace, '\\') . '\\';

$classes = get_declared_classes();
$interfaces = get_declared_interfaces();
$traits = get_declared_traits();
$functions = get_defined_functions();
$constants = get_defined_constants();

$filter = function ($items) use ($namespace, $include_sub_namespaces) {
$matched = [];
foreach ($items as $item) {
if (strpos($item, $namespace) === 0) {
if (
$include_sub_namespaces ||
strpos(substr($item, strlen($namespace)), '\\') === false
) {
$matched[] = $item;
}
}
}
return $matched;
};

return [
'class' => $filter($classes),
'interface' => $filter($interfaces),
'trait' => $filter($traits),
'function' => $filter($functions['user']),
'constant' => $filter(array_keys($constants)),
];
}

// define and declare
require_once('./def.php');

var_dump(get_namespace_items('\\foo\\bar', false));
var_dump(get_namespace_items('\\foo\\bar', true));

[def.php]
<?php

namespace foo\bar;

function f() {}

class C {}

abstract class A {}

trait T {}

interface I {}

const c = 1;
define('d', 2);

// sub namespace
namespace foo\bar\baz;

function ff() {}

結果

// sub namespace なし
array(5) {
["class"]=>
array(2) {
[0]=>
string(9) "foo\bar\C"
[1]=>
string(9) "foo\bar\A"
}
["interface"]=>
array(1) {
[0]=>
string(9) "foo\bar\I"
}
["trait"]=>
array(1) {
[0]=>
string(9) "foo\bar\T"
}
["function"]=>
array(1) {
[0]=>
string(9) "foo\bar\f"
}
["constant"]=>
array(1) {
[0]=>
string(9) "foo\bar\c"
}
}

// sub namespace あり
array(5) {
["class"]=>
array(2) {
[0]=>
string(9) "foo\bar\C"
[1]=>
string(9) "foo\bar\A"
}
["interface"]=>
array(1) {
[0]=>
string(9) "foo\bar\I"
}
["trait"]=>
array(1) {
[0]=>
string(9) "foo\bar\T"
}
["function"]=>
array(2) {
[0]=>
string(9) "foo\bar\f"
[1]=>
string(14) "foo\bar\baz\ff"
}
["constant"]=>
array(1) {
[0]=>
string(9) "foo\bar\c"
}
}
Gist のセルフフォークできなかった
Gist で 以前作ったものをベースに少し違ったものにしたいときがあります
機能追加やバグ修正みたいな更新とは違うので バージョン管理されていて以前のものに戻せるとはいえ 上書きして元のコードを置き換えてしまうのはちょっと違います
Gist は置けるファイル数が少なく フォルダ構造も作れないので 新しい Gist を作成してベースにしたい Gist からファイルを持ってくればいいのですが 面倒なのと完全に独立した別々のものになってしまうのもなぁと思います

ふと フォークすればできるんじゃないの? と閃きました
Gist は Github の機能ですし フォーク機能はあったはずです
それにフォークならフォーク元という情報が残って関連 Gist がわかるのも良いところです

早速試してみたのですが……フォークボタンがありませんでした
見覚えはあるのにと思って探すと 他人の Gist ならフォークボタンが出てきます
自分の Gist はフォークできないみたいです

このためだけにアカウントをもう一つ作って フォークするときだけ交互に使うというのはなんか違いますし 結局諦めて新しい Gist にファイルをコピーすることにしました
ShadowDOM 内の leaflet でデフォルトマーカーが使えない
leaflet を Custom Element の中で使うとデフォルトマーカーが表示されませんでした
コンソールを見ると 開いてる HTML のページと同じフォルダから探そうとしてるようです
CDN から leaflet をロードしてるのでそっちを見てほしいのですけど……

Custom Element を使わなければデフォルトマーカーが表示できているので どういうことなのか調べてみました
結果は Shadow DOM による CSS の分離が原因でした

アイコンパスを検出する関数はこれです
https://github.com/Leaflet/Leaflet/blob/v1.7.1/src/layer/marker/Icon.Default.js#L45

「leaflet-default-icon-path」 クラス付きの div を作って一時的に document.body に追加しています
その div の現在有効なスタイルの background-image に設定されたパスをもとにベースとなるパスを検出してるようです

leaflet の CSS がロードされていれば その CSS で設定された URL がもとになるので CDN からロードされるというわけです
しかし Shadow DOM を使っていて その中でのみ leaflet の CSS をロードしていると document.body に追加された要素にスタイルは適用されません

その結果 見つからず同じフォルダから探すことになっていました
document.body ではなく ShadowRoot などに置き換えられればよいのですが document.body でハードコーディングされているので 関数ごと置き換えることになります

import * as L from "https://unpkg.com/leaflet@1.7.1/dist/leaflet-src.esm.js"

L.Icon.Default.prototype._detectIconPath = () => {
return "https://unpkg.com/leaflet@1.7.1/dist/images/"
}

import のところに URL を書いてるので CSS を経由せずに直接書くことにしました

_detectIconPath の結果は IconDefault.imagePath に代入されるので

import * as L from "https://unpkg.com/leaflet@1.7.1/dist/leaflet-src.esm.js"
L.Icon.Default.imagePath = "https://unpkg.com/leaflet@1.7.1/dist/images/"

にもできます
ただ 互換性のために残されてますが IconDefault.imagePath は deprecated らしいので options.imagePath の方に設定するほうが良いかもしれません

import * as L from "https://unpkg.com/leaflet@1.7.1/dist/leaflet-src.esm.js"
L.Icon.Default.prototype.options.imagePath = "https://unpkg.com/leaflet@1.7.1/dist/images/"

CustomElement と leaflet の問題は以前もあった気がするのですが そのときはこういう対処をした覚えがないです
どうしたんだったっけ
Firefox 89 で見た目が変わった
新デザインと聞いていたのでアップデートしました
極端に変わってはないのですが 少し使った感じだと 個人的に前のほうが好きでした

タブが変に広くて縦幅取ってますし URL バーの左右には無駄な空白があります
あと Pocket やメニューなどのアイコンは小さくなって少し押しづらい感があります

何が良くなったのかはいまいちわからないのですが 統計情報を基によく使われる/使われない部分を分析して変更したらしいです
ほとんど使われてないところは非表示になったりです
「何が無くなったか」と言われると特に気づけませんでした

ちゃんと統計情報に基づいてデザイン決めてるだな~と感心した一方で 統計情報の送信って拒否してる人も多いですよね
Google とか Microsoft のサービスではよく送信するかを聞かれますが そういうこと聞かれたらイヤと言う人が多そうです
それも自由だと思いますが 統計情報を送らない以上 開発者側からはいないもの扱いなので自分はよく使う機能だったのに誰も使ってない機能と扱われて機能が消えてしまったりとかもありそうですよね
情報の送信を拒否するような人ほどマイナー機能まで使ってそうな印象ありますし
WSL の Ubuntu のアップグレードができない
WSL で使っていた Ubuntu が以前のバージョンのままだったのでアップグレードしようとしました
LTS 間でのアップグレードなので問題もないと思っていたのですが問題が起きました

「do-release-upgrade」 コマンドが途中で止まります
最後に表示されるメッセージはこれです

Reading state information... Done

何度試してもここで止まります
ログファイルを見てみてもエラーらしいものが見つかりません

以前に別の PC で Ubuntu を更新したときも同じコマンドを使ったはずで そのときは何も問題なく更新できたと思うのですけどね

ぐぐってみるとこんなページがありました
https://github.com/microsoft/WSL/discussions/3489

snapd を消すと動くという報告があります

apt remove snapd

これを実行してからもう一度 do-release-upgrade コマンドを実行してみます
すると 次に進みました!

上のページでは 18.04 へのアップグレードですが 私は 20.04 へのアップグレードでした
バージョン問わず do-release-upgrade 使う場合に起きる問題のようです

snapd を調べてみると パッケージマネージャでした
そういえば GUI インストールした Ubuntu ではこの折り紙みたいなアイコンを見たことがあります
この WSL 環境では GUI アプリを試しに使ってみたことがあり snapd が入ってたようです
以前問題なく更新できた環境だと GUI を入れてなかったから snapd も入ってなかったのかもしれません
C ドライブ全体のフォルダ共有
デフォルトで C ドライブなどドライブルートはフォルダ共有されています

\\172.23.116.130\c$\

みたいに共有名にドライブレターと $ を入れます
隠されているのでコンピュータ名だけを指定して共有一覧を見ても表示されていません

PowerShell の Get-SmbShare コマンドで共有リストを取得するとちゃんと含まれています

PS C:\Users\nexpr> Get-SmbShare

Name ScopeName Path Description
---- --------- ---- -----------
ADMIN$ * C:\WINDOWS Remote Admin
C$ * C:\ Default share
IPC$ * Remote IPC

C$ へのアクセスで使うユーザ情報は Administrator の必要があります
普段使いの管理者権限ユーザの情報ではログインできません

Administrator は無効になってることが多いので 使う場合は

コンピュータの管理 > ローカル ユーザとグループ > ユーザー

から Administrator を選んで

◯ プロパティを開いてアカウントを無効にするのチェックをはずす
◯ 右クリックからパスワードの設定

が必要です

設定すると Administrator と設定したパスワードで C$ を開けます

セキュリティ的にはあまり良くないと思いますが VM やサンドボックスのファイルすべてをホスト側のエクスプローラで操作したいときに使えます
Fedora のデフォルトエディタが nano になってた
https://fedoraproject.org/wiki/Changes/UseNanoByDefault

33 からだったようです
32 が最近 EOL を迎えてたので更新して気づきました

理由は git commit などのコマンドからエディタが起動したときに vi だと混乱する人が多いからのようです
これはすごくわかります
最近でこそ 保存と終了くらいはできるようになりましたが 私も慣れないころは vi が起動されると終了すらできなくて困りました
Ctrl-C は効かないし ググっても最初は意味不明だったのでタブごと閉じてたり……
CUI 環境だと強制再起動?

それに比べて nano は下に使い方が書いてるくらいのユーザフレンドリーさです
機能は多くないものの最低限の編集には十分です

デフォルトとするなら nano のほうが向いてると言えます
vi 使いたいような少数派の人たちは デフォルトエディタを変更するくらい苦もなくできるでしょうし

nano 使いからしては CentOS がインストール方法によってはデフォルトで nano が入ってないということに不満なのですが この変更が取り込まれる頃にはデフォルトインストールされることになりそうですね

やっぱり nano は最高なの
ページ遷移を防ぐモジュール
beforeunload イベントのリスナでページ遷移を防ぐ処理は 最近だと任意テキストを表示できないし 個別にリスナを登録する意味は特に無い
モジュールごとにリスナを登録していくと 後から強制的に移動させたい場合に解除が面倒
なので ページ遷移を防ぐ処理だけをモジュールにまとめた

let listener_added = false
const set = new Set()

const listener = (event) => event.returnValue = "preventer"

export const prevent = (key) => {
set.add(key)
if (set.size && !listener_added) {
window.addEventListener("beforeunload", listener)
listener_added = true
}
}

export const unprevent = (key) => {
set.delete(key)
if (!set.size && listener_added) {
window.removeEventListener("beforeunload", listener)
listener_added = false
}
}

export const forceUnpreventAll = () => {
for (const key of set) {
unprevent(key)
}
}

export const getReasons = () => [...set]

prevent 関数の引数にキーを入れて呼び出す
キーは任意の型で文字列とかシンボルとか
1 つ以上登録されてればページ遷移しようとするとダイアログが出る

unprevent 関数の引数にキーを入れて呼び出すと 対応する prevent を解除する

forceUnpreventAll 関数を呼び出すと全部の prevent を解除する
ただし このモジュール外で登録した beforeunload があれば ダイアログは出る

getReasons 関数でキーのリストを取得できる
現状がページ遷移がブロックされる状態かの判断や 何が原因でブロックされる状態なのかを確認できる

現状の実装だと prevent に登録されたキー数が 0, 1 の切り替わりでリスナの付け外しをしてるけど 常に付けておいて returnValue に代入するかを切り替えるでもよかったかも
IIAFE
関数即時呼び出しの IIFE の非同期版
読み方は IIFE の読み方がイッフィーだから IIAFE は イアッフィー ?
なんかテンション高そう
更新してなかった Windows10 にサポート終了通知が来てた
win10-expired

「お使いのバージョンの Windows は、サービスを終了しました。」
初めて見た
バージョンは 1909 でまだ最近な方だけど Windows10 のサポート期限は結構短いらしい

更新しようとしたら VirtalBox のアンインストールが必要って言われた

win10update-need-uninstall

別 PC は VirtualBox インストールしたまま 21H1 に更新できてるんだけど バージョンが古いのかな
それならアップデートが必要ですって言ってくれたらいいのに

とりあえず 最近は WSL2 + Docker で十分で VirtualBox は使ってないので削除して無事更新完了
グローバル IP を調べる方法
ローカルじゃなくてグローバルなので 外部のサーバと通信するしかない
普段は「ip 調べる」みたいなワードでググれば出てくる適当なサイトを見てる
だけど機械的にプログラムから取得したいときにはこれだと困る
IP を返してくれる API が必要

JSON ならここ
https://httpbin.org/ip

こういう JSON を返してくれる

{
"origin": "255.255.255.255"
}

IP アドレスだけなら JSON の必要もなくテキストでもいいので テキストでよければここ
https://checkip.amazonaws.com/

こんな感じでただのテキストとして取得できる

255.255.255.255

こっちは AWS のサービスなので それなりに安心
IPv6 でウェブページにアクセスする
「ipconfig /all」 を見ていたら IPv6 のアドレスを持ってました
IPv6 でアクセスしたこと無いなぁと思って IPv6 でアクセスを試してみました

普通に IPv4 感覚で http:// の後に IP アドレスを入れると URL と認識されず検索になってしまいます
そもそもポートの : と区別できないですし どうするんだろうと思って調べたところ URL では IP アドレスのところを [] で囲むそうです

http://[1000:2000:3000:4000:5000:6000:7000:8000]:3000/

この形式でアクセスしたら つながりました

localhost のアドレスは IPv4 で 127.0.0.1 のように IPv6 でも ::1 というのがあるので

http://[::1]:3000/

でもアクセスできます

curl を使う場合は [] が glob にならないように -g オプション(glob 無効化) が必要になるようです
ただ

curl http://[::1]:3000/

を試してみると問題なくリクエストできていました
curl のドキュメントの glob のところに IPv6 を除くようなことを書いてるので -g はなくてもよさそうです