Map や連想配列の内部実装
普段当たり前のように使ってる Map 機能
key に対して value をもつデータ構造です

最近の言語なら標準で使えることも多いですが 使えない環境でそれがほしいことがあったので自分で簡単に実装してみようかなと
単にデータの保持とアクセス方法を key-value 形式にしたいだけだったので パフォーマンスは気にせず配列に key と value を持たせるようにしました

JavaScript で書くとこういう感じでデータを保持しアクセスします

const data = [
{ key: "foo", value: 1 },
{ key: "bar", value: 2 },
]

const value = data.find(x => x.key === "bar")?.value

順番も保持できてますし データ数が少ないなら特に問題もないです
ですが 本来の Map 相当の機能はこんな実装はしないはず
100 万件のデータがあって一番最後のを取得すると 100 万件を探索することになりますし

考えてみるとどう実装してるのでしょうか?
昔 C とか低レイヤー言語を使ってたころにアルゴリズムの説明を薄っすらと読んだような記憶はありますが 具体的なことはおぼえてないです
ハッシュやハッシュマップみたいな呼ばれ方もするデータ構造ですし ハッシュ値を使ってるはずですが どう使ってるのでしたっけ?
key にそのまま使うなら 実装のために Map 相当の機能が必要になって矛盾します

MD5 や SHA みたいな文字列をイメージするから どう使うの?と疑問に思ってましたが本来は数値データのはず
文字なのは 16 進数などわかりやすく表現するからです
ハッシュ値を数値として使えば配列のインデックスアクセスが使えます

アクセスイメージ

const calcHash = (data) => {
return // 0 ~ 99 の数値を返す
}

const arr = Array(100)
arr[calcHash(key)] = value
console.log(arr[calcHash(key)])

これなら実現できそう と思ったものの ハッシュ値が 100 パターン程度しかないと重複しそう
100 万パターンくらいあれば 大抵のケースでは重複しなさそうですが 保存領域も 100 万パターン分確保するので配列の長さが 100 万になります
Map のインスタンス 1 つごとに 100 万もメモリを確保するのは かなりムダがあります
value にアドレスが入って 4 バイトだとしても 4MB ほど
Map インスタンスなんてたくさん作ることがよくあるのに 1000 個作れば 4GB って実用性なさすぎです

それに 100 万パターン用意しても運が悪ければ 数個で異なるデータのハッシュ値が重複することはありえるわけですし
これじゃダメそうですね

せっかくなので bing に聞いてみました
ハッシュ値の数値をインデックスにするところまではあってたようです
重複は避けられないので 大きすぎる領域を確保するのではなく 重複したときも問題なく動くようにするという方法でした
2 種類方法があるようです

○ インデックスアクセスで参照できるデータをリスト型として 同じハッシュ値だったデータを全部まとめて持つ
○ すでにデータが入っていたら再ハッシュして別のハッシュ値を割り当てる

言われてみると聞き覚えがあるような
でも これだとどっちの方法でも 異なるデータで同じハッシュ値になったら 同じなのに元データが異なるってことをどうやって判断するの?と疑問が出てきます
ただこれは key そのものも保持していて key が一致するかを順番にチェックするようです

ハッシュ値が常に 0 だったら 最初に書いたコードみたいなことになります
これをハッシュ値で分散してる分 一致チェックする量が減るということのようです

スクリプト言語など高レイヤーの言語ばかり書いてると低レイヤー部分はけっこう忘れてますね
finally の return が扱いづらい
めったに使わない finally を久々に使ったらイマイチな挙動がありました
finally の中で return します

const fn = (a) => {
try {
return 1
} finally {
if (!a) return
if (a.value > 10) {
return 2
}
}
}

return した場合は finally の return が優先されます
空の return でもです
しかし return がない場合は本来の return の値が return されます
こういう結果になります

fn()
// undefined
fn({ value: 100 })
// 2
fn({ value: 0 })
// 1

わからなくはない挙動ですが 空 return と return なしの違いは分かりづらいです
finally の中で return することはあまりない上にしないほうがいい気はしますけど 例外的な値の場合に早期 return は結構あると思うのでそれで return が上書きされるのは扱いづらいです
finally の中は関数を呼び出すだけにして その関数の処理で早期 return するとかしたほうがいいのかもしれませんね
つい忘れて return を書いて思わぬ結果になりそうですし



ちなみに Promise の finally だと return は無意味です

const fn2 = (a) => {
return Promise.resolve(1).finally(() => {
if (!a) return
if (a.value > 10) {
return 2
}
})
}

await fn2()
// 1
await fn2({ value: 100 })
// 1
await fn2({ value: 0 })
// 1

return を上書きしたいみたいな特殊なことは少ないですし こっちのほうがいいですね
もし Promise で finally の処理で return 値を書き換えたいなら then の成功失敗両方に同じ関数を渡せばできます
ただ return を書いたかどうかによるわかりづらい違いはないです
本来の return 値を返したいなら明示的に指定する必要があります

const fn3 = (a) => {
const finallyFn = (value) => {
if (!a) return
if (a.value > 10) {
return 2
}
return value // ←これが必要
}
return Promise.resolve(1).then(finallyFn, finallyFn)
}

await fn3()
// undefined
await fn3({ value: 100 })
// 2
await fn3({ value: 0 })
// 1
JavaScript のレコードとタプル
久々に TC39 の Proposal を見ていて思ったのですが ステージ 2 で止まったままなのが溜まってきてますね
ふとタプル・レコードってどうなってるんだろうと見てみました
https://github.com/tc39/proposal-record-tuple

構文は

#{}
#[]

らしいです

短く書ける点ではいいですが見た目的にはなんか微妙です
個人的には

{| |}
[| |]

が良かったです

構文の議論はかなり長く続いていて {| |} も提案されていて賛成も多いようなのですが #{} に決めたようです
その他の提案だと既存を壊すとかパーサの問題とかあるようですが これは明確にダメと言う理由もなく好みの問題で選ばれなかったみたいです
https://github.com/tc39/proposal-record-tuple/issues/10

1 行で複雑にネストされたら見づらいという意見がありますが #{} だって見づらいし変わらないと思うのですけどね
重要なのは末尾との対称性であって 数十行と続く長いデータがネストするとき 末尾だけでなにかわかるほうがメリットがあると思うんです
「}」 だったら if などのブロックもオブジェクトも関数も同じなのでどのカッコなのかわかりづらいですし
# が見やすいというなら

{# #}
[# #]

でもいいと思います

また ネストする場合は明示的にすべての階層で # を書かないとだめのようです

#{ a: 1, b: #{ c: 2 } }

「{| |}」 形式なら 外と内で形式違うのは気持ち悪いのでそれでもいいと思います

{| a: 1, b: { c: 2 } |}

{| a: 1, b: {| c: 2 |} |}

ですが # プレフィックスだと

#(
{ a: 1, b: { c: 2 } }
)

みたいに プレフィックスとオブジェクト部分で分けて見れるので # のあとは普通のオブジェクト形式で書けばいいとしてもいいと思うのですけどね

#{ a: 1, b: { c: 2 } }

あと結構不便そうな部分でプリミティブ値しか含められないそうです
こういうことはできません

const obj = { a: 1 }

const r1 = #{ obj }
const r2 = #{ obj }
r1 === r2 // true

レコードの obj プロパティが固定であれば obj の中身が変更できても r1 と r2 が等しいことに変わりはなく === で比較できるので この制限はいらないと思います

あとそもそもの レコード・タプルの不変という部分ですが 使う側で不便になりそうかもとも思ってます
すでにクラスのプライベートプロパティで 外から読み書きできないものがあります
これが使われてるライブラリを使うときに デバッグしづらいし 外から読み書きが必要なのにできないし 実行時の書き換えで対応できずライブラリを直接書き換えるしかなくてすごく使いづらいです
レコードもライブラリがこの型で持たれていると使う側で無理やり書き換えて対処したいところでできないという不便なケースが出てきそうです
JavaScript はその辺がゆるくソースコードを変えなくても実行時に無理やり置き換えて対処できるのが良いところでもあるのに それができなくなるのはデメリットでもあるように思います

使う側でその辺のゆるさを設定できればいいのですけどね
ストア版 WSL で systemd から起動する Docker が強制終了後に起動しなくなる
環境はストア版 WSL (Ubuntu) で systemd から Docker を起動します
インストール直後はなんの問題もなく docker-ce をインストールして systemd から起動するだけで普通に使えていたのですが 強制終了されてから動かなくなりました
再起動の原因はストア版 WSL が勝手に更新されること
更新時にそのアプリが終了されるので WSL が実行中だと強制終了されてしまいます
その後に WSL を起動して Docker を使おうとすると動きません

docker コマンドを実行して docker サーバーと通信しそうなところで反応がなくてずっと応答が返ってこない状態です
systemctl status で見てみると inactive らしいですが ランプは緑色でよくわからない状態です
停止してみようにも失敗します
sock ファイルのせいでアクティブなままになってるみたいな警告が出てました
sock ファイルは残ってたので削除して containerd も停止してから再起動してみました
これでも解決しません
WSL ごと停止して再起動してみても同じ状況になります
sock ファイルは再度作られていて journalctl でログを見ると 一旦起動してすぐ終了してる感じです

この現象はこれまでも数回起きていて docker を再インストールすれば直ってます
アップデートでもパッケージを入れ直すことになるみたいなので アップデートでもいいです
発生頻度は数ヶ月に 1 回で その頃には更新があることも多いのでアップデートで対処してることが多いです

はっきりした原因がわからないのが気持ち悪いですが とりあえずの対処方法はわかるので今のところこれで対処してます
ただ今回はアップデートで直ったものの docker-ce は更新対象に含まれてなかったです
containerd は含まれていたのでこっちが原因なのかもしれないですが containerd は再起動後も問題なく動いていて stop/start でもエラーもなかったです

少し気になってるのはストア WSL の更新で強制終了してるというところです
「wsl --shutdown」 で強制停止は何度かあるはずですが このときは Docker に問題が起きてない気がします
WSL 自体の更新のせいでコンテナの仕組みに影響してる ということもありえるのでしょうか
再インストールすれば最新版に更新されますし アップデートでその変更に対処してるのかもしれません
ストア版にしてから発生というのも ストア版になるまでは WSL 自体の更新はほぼなかったので気づかなかっただけなのかもです

結構ありえるかもと思ってみたものの アップデートしないとコンテナが動かなくなるような変更を何度もするかという疑問がありますし 変更履歴を簡単にみても WSL 対応みたいなのはなかったです



その後 また同じ状態が発生しました
ただ今回は前と違って containerd が停止しています
起動しようとしても反応がなく 1 分程度待っても終わりません
起動コマンドは強制終了して再インストールを試しました
期間が短いこともあって containerd も docker-ce も最新版で apt のアップデートはなかったので再インストールです
しかし インストール直後のセットアップ処理で起動しようとするからか containerd のセットアップで固まったままになります
強制終了して何度か試しても同じ結果です

固まった状態で CPU の使用率や IO の負荷を見ても特に負荷が高いわけでもなく 何もしてない待機状態みたいです
事前に起動しないといけないサーバーとかはないと思うのですけど
WSL 自体の再起動も試しましたが変わりません

処理が重たくて時間がかかってるわけじゃないなら なにかの対処をしないと改善しなさそうだけど 原因が見当たらないです
半分あきらめて 固まったまま放置していて 数十分後に見てみるとなぜかコマンドが完了していました
containerd のステータスを見ると起動しています
docker も問題なく起動できました

やっぱり原因は不明ですが containerd の起動で進まないならしばらくそのまま放置で起動できる場合もあるようです
Import Assertions の名前が変わった
TC 39 のミーティングが 3 月下旬にあったので 何が変わったのかなと見てみました
https://github.com/tc39/proposals

変更があったのはステージ 1 や ステージ 2 ばかりで ステージ 3 やステージ 4 への追加はないようです
ステージ 2 に上がったものでは Iterator.range があります

こういう感じで使えるものみたいです

for (const i of Iterator.range(0n, 43n)) console.log(i)

今でも

for (const i of Array(10).keys()) console.log(i)

とすれば 0 ~ 9 を i としてループできるので 別になくてもいいかなというものです
ただ開始番号やステップを指定したり減らしていくことはできないので そういうことをしたいときには少しは便利になるのかも
また一旦配列を作らないので終わりがない範囲を扱えるメリットがあります
今の方法でも配列の実体は作らず .keys() でイテレータを作るので 10 億とかあっても最初の数件を取得なら一瞬で終わります
しかし 配列の要素数には上限があるので 4294967295 が最大値となってしまいます

他には Float16Array がステージ 2 みたいです
言われてみれば 16 はなかったですが 32 と 64 だけあれば十分で欲しいと思ったこともないので個人的には上のイテレータ以上にどっちでもいいものです

良さそうなのはまだステージ 1 に追加された段階ですが Promise.withResolvers です
Promise ってコンストラクタに渡す関数が resolve/reject を受け取り その関数の中で成功失敗を判断しどちらかを呼び出すのが前提な作りです
でもそうじゃない使い方をすることも少なくないです
そういうときは

let resolve
let reject
const promise = new Promise((a, b) => {
resolve = a
reject = b
})

みたいな一手間が必要です
割とよくやるものですし ライブラリを見ててもこういうことをしてるのは結構あります
これを

const { promise, resolve, reject } = Promise.withResolvers()

のように書けるようにするという提案です
あると助かりますし 早いうちにステージ 4 まで来てほしいですね

前回 ステージ 3 からステージ 2 に格下げされた Import Assertions ですが Import Attributes に名前が変わりました
構文も変わって assert から with になりました

import json from "./foo.json" with { type: "json" }

当面は assert の構文も残るようですが その他消された機能のようにしばらくして with が普及した頃には消されそうです
古い機能は何があろうと残そうとするのに 最近は簡単に消していくのはやめてほしいものです

長いので読んでませんが 議論詳細はここで見れます
https://github.com/tc39/notes/tree/main/meetings/2023-03
HTML タグ一覧 (2023/3)
search タグが仕様に追加されたと聞いたので以前書いた HTML のタグリストの新しい版
whatwg の目次から抽出

The document element
html

Document metadata
head
title
base
link
meta
style

Sections
body
article
section
nav
aside
h1
h2
h3
h4
h5
h6
hgroup
header
footer
address

Grouping content
p
hr
pre
blockquote
ol
ul
menu
li
dl
dt
dd
figure
figcaption
main
search
div

Text-level semantics
a
em
strong
small
s
cite
q
dfn
abbr
ruby
rt
rp
data
time
code
var
samp
kbd
sub
sup
i
b
u
mark
bdi
bdo
span
br
wbr

Edits
ins
del

Embedded content
picture
source
img
iframe
embed
object
video
audio
track
map
area

Tabular data
table
caption
colgroup
col
tbody
thead
tfoot
tr
td
th

Forms
form
label
input
button
select
datalist
optgroup
option
textarea
output
progress
meter
fieldset
legend

Interactive elements
details
summary
dialog

Scripting
script
noscript
template
slot
canvas

全部で 112 個

前回の 106 から 6 つ増えてるけど 実際の差分は少し
前回はセクション名をそのまま使っていたので h1 ~ h6 や sub と sup が 1 行にまとまっていたけど 今回はそれぞれを 1 行にしてる

追加されたのは search 要素
消えたのは param 要素

section 16 に廃止機能がまとまっていて param はこっちに移動したみたい
このセクションの中だと marquee だけが他の有効な要素と同じ感じでセクション名に載ってる
そのせいで自動的に取り出したときに marquee も含まれてた
仕様に載ってるんだしとリストに入れておこうとしたけど セクションの中身を見ると他にも param とか frame とかの廃止要素も色々
入れるならそれらも入れないとバランスが悪い気がするけど 廃止要素も全部探すのは面倒なので section 16 は除外



ついでなので Chrome 上でコンストラクタがある要素 (Chrome 112 時点)

const ctors = Object.getOwnPropertyNames(window).filter(x => x.match(/^HTML.+Element$/)).toSorted()
copy(ctors.join("\n"))
HTMLAnchorElement
HTMLAreaElement
HTMLAudioElement
HTMLBRElement
HTMLBaseElement
HTMLBodyElement
HTMLButtonElement
HTMLCanvasElement
HTMLDListElement
HTMLDataElement
HTMLDataListElement
HTMLDetailsElement
HTMLDialogElement
HTMLDirectoryElement
HTMLDivElement
HTMLEmbedElement
HTMLFieldSetElement
HTMLFontElement
HTMLFormElement
HTMLFrameElement
HTMLFrameSetElement
HTMLHRElement
HTMLHeadElement
HTMLHeadingElement
HTMLHtmlElement
HTMLIFrameElement
HTMLImageElement
HTMLInputElement
HTMLLIElement
HTMLLabelElement
HTMLLegendElement
HTMLLinkElement
HTMLMapElement
HTMLMarqueeElement
HTMLMediaElement
HTMLMenuElement
HTMLMetaElement
HTMLMeterElement
HTMLModElement
HTMLOListElement
HTMLObjectElement
HTMLOptGroupElement
HTMLOptionElement
HTMLOutputElement
HTMLParagraphElement
HTMLParamElement
HTMLPictureElement
HTMLPreElement
HTMLProgressElement
HTMLQuoteElement
HTMLScriptElement
HTMLSelectElement
HTMLSlotElement
HTMLSourceElement
HTMLSpanElement
HTMLStyleElement
HTMLTableCaptionElement
HTMLTableCellElement
HTMLTableColElement
HTMLTableElement
HTMLTableRowElement
HTMLTableSectionElement
HTMLTemplateElement
HTMLTextAreaElement
HTMLTimeElement
HTMLTitleElement
HTMLTrackElement
HTMLUListElement
HTMLUnknownElement
HTMLVideoElement

70 種類

一部要素は共通のコンストラクタだし↓

document.createElement("del").constructor.name
// 'HTMLModElement'
document.createElement("ins").constructor.name
// 'HTMLModElement'

独自のコンストラクタを持たない要素もあるし↓

document.createElement("b").constructor.name
// 'HTMLElement'
document.createElement("article").constructor.name
// 'HTMLElement'

HTMLUnknownElement も含まれてるしで HTML タグの数と一致はしてない
Chrome のリリースチャンネルに Early Stable が増えてた
Chrome Platform Status によれば Chrome 112 の Stable リリースの予定日は 2023/3/29 です
https://chromestatus.com/roadmap

ですが 3/29 時点では更新をチェックしても更新なしでした
時差を考慮して 1 日待ってみても更新なしです
各言語版は更に遅れるのかもとさらに数日待ってみても更新がないです
新規インストールを試してみても 111 です

遅延でもしたのかなと調べてみたら 110 からリリーススケジュールに変更があったようです
Stable リリースの前に一部のユーザーだけに公開される Early Stable というものが追加されたみたいです
https://developer.chrome.com/blog/early-stable/
すでに Beta/Dev/Canary といろいろあるのにまだ増えるのか

Chrome Platform Status の Stable リリース日はこの Early Stable の日付になってるようです
Chromium Dash の方のスケジュールだと Early Stable と Stable の日付が別々に記載されていました
https://chromiumdash.appspot.com/schedule

一般の Stable リリースは 4/4 で ほぼ一週間ずれるようです
Beta 版があるといっても 積極的にこれを使って既存ページへの影響をテストしてる人は少ないでしょうし いつも Stable 版が出てから色々問題に気づいてるので 一部ユーザーにだけ公開して問題があったら本リリースを遅らせるというのはわからなくないです
ただ新しくバージョンを増やさず Beta 版でそれやればいいのではとも思います

Chrome 112 では待望の CSS Nesting が使えるようになります
CSS のビルドはほぼこれのためだけにやっているようなものだったので これで CSS はビルド不要になりますね
ビルド不要だと ESM から直接ロードしてモジュール単位で使えるのもいいところです
ただ残念ながら Safari はまだのようです
1 月に TP で CSS Nesting がリリースされていて ちょっと前に 16.4 のリリースがあったので Chrome に合わせて CSS Nesting が含まれてるのだろうなと思ってたのに含まれてなかったです
Safari 対応するならもうしばらくは ネストなしかビルドする必要があります
maxlength と絵文字
input の属性の maxlength で文字数制限してたら 4 byte 絵文字は 2 文字分扱いだった
JavaScript の length に合わされてるみたい

絵文字が使われることが増えてきてるし 日本語を 1 文字扱いするところだと絵文字も 1 文字扱いのほうが自然だからそうしたいけど そうなると maxlength が使えず JavaScript で制御するしかなさそう

それに絵文字も複雑化していて

(人っぽい絵文字(4byte)) + (U+200D) + (♂or♀)

で性別違いの派生版が出せるものがあったりと 見た目と内部文字数が一致しない

"🧝‍♀".split("")
// (4) ['\uD83E', '\uDDDD', '‍', '♀']

[..."🧝‍♀"]
// (3) ['🧝', '‍', '♀']

[..."🧝‍♀"].map(x => x.codePointAt().toString(16))
// (3) ['1f9dd', '200d', '2640']

もっと長いものだと

"👨‍👩‍👧‍👦".split("")
// (11) ['\uD83D', '\uDC68', '‍', '\uD83D', '\uDC69', '‍', '\uD83D', '\uDC67', '‍', '\uD83D', '\uDC66']

[..."👨‍👩‍👧‍👦"]
// (7) ['👨', '‍', '👩', '‍', '👧', '‍', '👦']

[..."👨‍👩‍👧‍👦"].map(x => x.codePointAt().toString(16))
// (7) ['1f468', '200d', '1f469', '200d', '1f467', '200d', '1f466']

これは文字幅も 3, 4 文字分くらいになってる

U+200D があれば前後をまとめて一つとみなせばいいので自分で計算できなくなさそうだけど 無効な結合の場合もある
"あ\u200dい" みたいなケース
さすがにこういうのの判断を自分でしたくない
Intl.Segmenter でセグメント分けすると正常な絵文字結合なら 1 セグメントとしてくれる
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl/Segmenter

[...new Intl.Segmenter().segment("abc あ\u200dい🦝🧝‍♀👨‍👩‍👧‍👦")]
// (9) [...]
// 0: {segment: 'a', index: 0, input: 'abc あ‍い🦝🧝‍♀👨‍👩‍👧‍👦'}
// 1: {segment: 'b', index: 1, input: 'abc あ‍い🦝🧝‍♀👨‍👩‍👧‍👦'}
// 2: {segment: 'c', index: 2, input: 'abc あ‍い🦝🧝‍♀👨‍👩‍👧‍👦'}
// 3: {segment: ' ', index: 3, input: 'abc あ‍い🦝🧝‍♀👨‍👩‍👧‍👦'}
// 4: {segment: 'あ‍', index: 4, input: 'abc あ‍い🦝🧝‍♀👨‍👩‍👧‍👦'}
// 5: {segment: 'い', index: 6, input: 'abc あ‍い🦝🧝‍♀👨‍👩‍👧‍👦'}
// 6: {segment: '🦝', index: 7, input: 'abc あ‍い🦝🧝‍♀👨‍👩‍👧‍👦'}
// 7: {segment: '🧝‍♀', index: 9, input: 'abc あ‍い🦝🧝‍♀👨‍👩‍👧‍👦'}
// 8: {segment: '👨‍👩‍👧‍👦', index: 13, input: 'abc あ‍い🦝🧝‍♀👨‍👩‍👧‍👦'}

無効な結合文字は除外される
セグメント数を数えれば絵文字を 1 文字とした文字数になる

けどそこまでするものなのかなという気持ちもある
変な工夫をせず絵文字はそういうものと考えて単純に length 換算でいいのかも

DB だとどうなるか気になったので 一応 PostgreSQL で実行してみたら
絵文字単体は 4byte でも 1 文字扱い
結合文字が入ると別文字扱いで JavaScript で [..."(絵文字)"].length した場合と同じ

これでいいかな
Chrome 112 で FormData のコンストラクタに引数が追加される
2 つめの引数に submitter が追加されるみたい

https://chromestatus.com/feature/5066604734316544
https://xhr.spec.whatwg.org/#interface-formdata

これまでの FormData ではサブミットボタンは対象外でした

<form id="form1">
<input name="text" value="a">
<button id="submit" type="submit" name="submit" value="1">submit</button>
</form>

という HTML があるときに このフォームを FormData 化して中身を見ると

const fd = new FormData(form1)
console.log(Array.from(fd.entries()))
// [
// ['text', 'a']
// ]

となっていて submit は含まれません

2 つめの引数の submitter に submit ボタンを渡すと

const fd = new FormData(form1, submit)
console.log(Array.from(fd.entries()))
// [
// ['text', 'a']
// ['submit', '1']
// ]

という感じで含まれるようになります

true/false じゃなくて HTML 要素を渡すのはフォームの挙動に合わせるためです
フォームにはサブミットボタンを複数配置できます
その中で押されたボタンの情報だけが送信されます
このフォームの動きに合わせて どのサブミットボタンを押されたとするかを指定するためです

<form id="form1">
<input name="text" value="a">
<button id="submit1" type="submit" name="submit" value="1">1</button>
<button id="submit2" type="submit" name="submit" value="2">2</button>
<button id="submit3" type="submit" name="submit" value="3">3</button>
</form>
const fd = new FormData(form1, submit2)
console.log(Array.from(fd.entries()))
// [
// ['text', 'a']
// ['submit', '2']
// ]

個人的には フォームから FormData をつくることはほぼなくて JavaScript の変数上に持っているデータから作ることばかりな上に フォームに複数のサブミットボタンを配置することもほぼないので使う機会は特になさそうです
従来のフォームで作られた画面でサブミットは fetch を使ってバックグラウンドでやるように変更したい みたいな場合には便利になるのかもですね
PostgreSQL の SELECT は列が空でもよかった
以前書いたコードを見てたとき

SELECT FROM table;

と言う感じで SELECT のあとに列名が抜けてるのがあった
「*」 が抜けてて動いてなさそう……と思ったけど実行されてるはずのコード
psql で試しに実行してみたらエラーにならず成功した

列名は省略可能みたい
昔からそうだっけと思って調べたら 9.4 からの変更だった

https://www.postgresql.jp/document/9.3/html/sql-select.html
https://www.postgresql.jp/document/9.4/html/sql-select.html

リリースノートには この変更で 0 個の列のビューのダンプ・リストアが正常に出来るようになるって書いてるから このために許可したみたい
https://www.postgresql.jp/document/9.4/html/release-9-4.html

列なしの SELECT なんてしないと思ったけど実際に使われていたわけで EXISTS の中で使う SELECT 文には 「*」 書かなくていいからありなのかも

ちなみに SQL 標準ではないみたいで mysql だと構文エラーでした
フォーマッターの行幅はインデントを除外した長さを対象にしてほしい
たいていのフォーマッターは 1 行の文字数を設定できます
指定値を超えると適当なところで自動で改行されます

80 文字だと少なすぎて インデントが深くなるところだとすぐ折り返されて逆に見づらくなります
なので 120 文字くらいにしてるのですが インデントが深くならないところだと長すぎてこれも見づらいです
トップレベルや 1 段階のインデントで 120 文字だと結構ありますからね
それくらいの長さのメソッドチェーンや配列だと折り返したくなります

元コードの折り返しを優先してくれるフォーマッターなら問題ないのですが それを無視して指定文字数に収まるなら改行を消すようなフォーマッターもあります
そう考えると 行幅の数え方を左端からじゃなくてインデントを除外してから数えるのがちょうどいい気がしますね
インデント除外で 80 文字くらいだとインデントの深さを気にせずいつでもちょうどいい長さになりそうです
Windows ってファイルを開いてると消せないと思ってたけど
Windows だとファイルを開いたままにしてると そのファイルを消せないと思ってた
なにかのプロセスで開いたままになってて消せなくて そのプロセスを見つけて停止することってよくあるし

でも普通に消せるみたい
Node.js で stream で書き込み中にエクスプローラーからファイルを消してみる

const stream = require("fs").createWriteStream("foo.txt")

stream.on("error", err => console.log({ err }))

setInterval(() => {
stream.write(
Date.now() + "\n",
(err, result) => console.log({ err, result })
)
}, 1000)

↑で毎秒書き込むので適当なところでファイルを消す
例のエラーはなく 普通に消せる

消したあとに ファイルを開いたプロセスが書きこむとどうなるか気になったけど 何も起きないみたい
書き込みは成功扱いで特にエラーは起きない
書き込み時に新規にファイルが作られるわけではなく どこにも出力されない
Linux での扱いと同じみたい

ということは消せないエラーが出てくるものはわざわざ追加でロックするような処理をしてる?
言語によってはデフォルトの挙動がそうなってるのかも

Windows だと OS やファイルシステムの都合で開いてるファイルは消せないものだと思ってたけど できるんだったらこの動きをデフォルトにしてほしい
どこかで開いてるからってエラー出されても面倒なだけで このエラーを見るたび Linux のほうがこういうところは便利だな―と思ってるくらいなので
オブジェクトと配列の記法のカッコの内側のスペース
const {prop1, prop2} = obj
const [item1, item2] = arr

というコードを dprint や Prettier などでフォーマットするとデフォルトだと

const { prop1, prop2 } = obj
const [item1, item2] = arr

{} だけ内側にスペースが入ってる非対称感が気になる
オブジェクトは

const { foo: bar } = { foo: 1 }

のように 1 要素に key と value みたいな複数を書くから前後の空白がある方が見やすいとか?
でも配列だって

const [a = 1, b = 2] = [10]

って書いたら同じようなものだと思うけど

ちなみに console.log での出力の場合 ブラウザ (devtools) 上ではオブジェクトも配列も前後にスペースなし
Node.js だとどっちもスペースあり

> console.log({a:1})
{ a: 1 }

> console.log([1,2])
[ 1, 2 ]
VSCode のタブアイコンが見づらくなった
最近のアップデートでタブアイコンが見づらくなりました
空白文字を表示する設定にすると スペースやタブを「・」や「→」で表示してくれてわかりやすくなります

以前までは「→」が大きくて見やすかったのに小さくなりました

vscode-tab-char

見づらすぎです
タブよりスペース派が多いらしいですし タブの見やすさは重視されていないんでしょうか

設定でもとに戻せないかなと探してみると 戻せました

Editor: Experimental Whitespace Rendering」 という設定です
svg / font / off の 3 つの選択肢があってデフォルトは svg になっています
これを off にするとこれまでのアイコンに戻ります
POST データが最後まで届かないと思ったら 受け取り切らずにレスポンスを返してるからだった
POST リクエストでペイロード部分をサーバー側でちゃんと受け取れてないケースがあって 原因調査のためにあれこれやってると再現したりしなかったり
余計なライブラリなしで 再現性がある程度あるものが用意できた と思ったら別原因で別の問題になってました

その時のコードがこんなの

[server.js]
require("http").createServer((req, res) => {
let size = 0
req.on("data", (chunk) => {
console.log("DATA", chunk.length, size += chunk.length)
})
req.on("end", () => {
console.log("END")
})
req.on("error", (err) => {
console.log("ERROR", err)
})
res.end("ok")
}).listen(8000)

[client.js]
const fs = require("fs")
const { request } = require("http")

const data = "abcd".repeat(1024 * 100)
fs.writeFileSync("tmp", data)

setInterval(() => {
const req = request({
hostname: "localhost",
port: 8000,
path: "/",
method: "POST",
headers: {
"Content-Type": "text/plain",
},
}, res => {
const buffers = []
res.on("data", (chunk) => {
buffers.push(chunk)
console.log("RES DATA", chunk.length)
})
res.on("end", () => {
const response = Buffer.concat(buffers).toString()
console.log("RES END", response)
})
})

req.on("finish", () => {
console.log("REQ FINISH")
})
req.on("error", (err) => {
console.log("REQ ERROR", err)
})

fs.createReadStream("tmp").pipe(req)
}, 1000 * 10)

fetch ではなく request を使っていたり 一旦ファイルに書き込んで readable stream を pipe しているのは 元の現象が発生したときに使っていたライブラリの内部実装に合わせたためです

出力は

[client]
> node .\client.js
REQ FINISH
RES DATA 2
RES END ok
RES DATA 2
RES END ok
RES DATA 2
RES END ok
RES DATA 2
RES END ok
RES DATA 2
RES END ok

[server]
> node .\server.js
DATA 65390 65390
DATA 146 65536
DATA 65381 130917
DATA 155 131072
DATA 65372 196444
DATA 164 196608
DATA 65363 261971
DATA 173 262144
DATA 65354 327498
DATA 182 327680
DATA 65345 393025
DATA 191 393216
DATA 16384 409600
END
DATA 65390 65390
DATA 146 65536
DATA 65381 130917
DATA 155 131072
DATA 65372 196444
DATA 164 196608
DATA 65390 65390
DATA 146 65536
DATA 65381 130917
DATA 155 131072
DATA 65372 196444
DATA 164 196608
DATA 65390 65390
DATA 146 65536
DATA 65381 130917
DATA 155 131072
DATA 65390 65390
DATA 146 65536
DATA 65381 130917
DATA 155 131072
DATA 65372 196444
DATA 164 196608

分かりづらいですが 5 回分のリクエストがあります
1 回目は正常なのですが 2 回目以降はサーバー側に途中までしかデータが届いていません
DATA の行の 2 つめの数字がそのリクエストの合計バイト数なので 前の行より減っているところからが次のリクエストです
クライアント側ではレスポンスは受け取れていますが finish イベントが起きなくなっています

なんどかプロセスを再起動してみても最初だけが成功します

接続したままになってるのかと思って socket を見てみると 途中までの場合でも毎回ちゃんとクローズしてるようです
サーバー側のリクエストハンドラにこういうコードを追加します

	req.socket.on("close", () => {
console.log("SOCK CLOSE")
})
(略)
DATA 16384 409600
END
SOCK CLOSE

(略)
DATA 164 196608
SOCK CLOSE

(略)
DATA 164 196608
SOCK CLOSE

原因不明で苦戦していたのですが すごく単純な理由でした
サーバー側のリクエストハンドラではペイロードを取得するためのイベントリスナを設定してはいますが レスポンスは同期的に即返しています

レスポンスを返してしまってるので もう残りのリクエストのペイロードを送信する必要はないと判断して途中で切断してしまっているみたいです

	res.end("ok")

を req の end イベントのハンドラ内に移すと この現象は発生しなくなりました
PHP で正規表現が全滅してた
PHP の OSS アプリケーションを動かそうとしたら謎のエラー
ドキュメントの手順どおりにやったはずだし 以前も使ったことがあるもので そのときは出た覚えのないエラー
新しいバージョンのみで発生?
エラーでググっても全然情報は無し

よくわからないし 中身まで見たくないしと動いてる環境からフォルダをコピーして来て 環境固有の設定だけ変更
これでも変わらず発生する
PHP バージョンはどっちも 8.0

仕方なくエラーが起きてるファイルと行数から中身を見てみることに
エラーの原因は null が来るはずないところで null が来てるせいみたい
null が想定されてないので null を渡すとエラーになる関数に null を渡して実行時エラー

どこで null が混入してる?と探してみると正規表現を使う関数で null が返ってきてる
引数を dump して見てみても どう考えても null になりそうにはないんだけど

コマンドラインで同じ処理を実行してみると warning が出た

preg_replace(): Compilation failed: unrecognised compile-time option bit(s) at offset 0

このせいで null になってるみたい
だけど 見たこと無いエラー
正規表現のコンパイルエラーだから不正な正規表現を入れると起こりそうだけど 特に問題ないはず
というかこれでも出る

echo preg_replace('/a/', 'b', 'abc');

PHP がバグってる?

こっちの warning でググってみると PHP ではなく libpcre の問題みたい
https://stackoverflow.com/questions/70040287/php7-4-preg-replace-compilation-failed-unrecognised-compile-time-option-bi

AlmaLinux9 だったので

dnf upgrade pcre2

で更新したら 10.40 になって エラーも出なくなった
FormData で送ると PHP の php://input で受け取れない
FormData で送るとテキストだけでも multipart/form-data 形式で送信されるようです
自分で Content-Type を指定しなくても自動で

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryEeFLLVciEeJfWzFZ

みたいなヘッダーが追加されます

また PHP で生の POST データを受け取るための php://input は multipart 非対応らしいです
https://www.php.net/manual/ja/wrappers.php.php

送信データにファイルが無い場合でも multipart なら受け取れません
そのせいで FormData で送ると php://input でデータを受け取れないということになります

以下確認用

↓のページを用意

<?php

var_dump($_POST);
var_dump(file_get_contents('php://input'));

● 普通の form としての POST を実行してみる

fetch("", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: "a=1" })

レスポンス:
array(1) { ["a"]=> string(1) "1" } string(3) "a=1"

⇨ $_POST と php://input の両方で受け取れてる

● FormData で POST してみる

const fd = new FormData()
fd.append("a", "1")
fetch("", { method: "POST", body: fd })

ペイロード:
------WebKitFormBoundary7KvCNMsEQGTuSHn7
Content-Disposition: form-data; name="a"

1
------WebKitFormBoundary7KvCNMsEQGTuSHn7--

レスポンス:
array(1) { ["a"]=> string(1) "1" } string(0) ""

⇨ $_POST でだけ受け取れてる
Chrome のヘッドレスモードが新しくなるみたい
https://developer.chrome.com/articles/new-headless/

これまでの Chrome のヘッドレスモードって Chrome の 1 機能だったので 普通に Chrome を使うのと同じ仕組みで動いていて ただ画面が無いだけだと思っていたのですが違ったようです
ヘッドレスとヘッドフルで別実装のブラウザになっていたそうです
そのせいで片方でのみ起きるバグがあったりメンテコストがあったりと問題があり Chrome 112 では統合されるようになったそうです

どっちを使うかはオプションで切り替えれて 今年後半に古い方は削除される予定だとか

chrome --headless=new
chrome --headless=old

実装が違っていたわけですし 新しい方にすると結果が変わってしまう可能性もあると思いますが そこはスタンドアロンバイナリとして古い方を使えるようにしてくれるそうです

個人的にはそのスタンドアロンバイナリのほうが使い道あったりと思ってたりします
ヘッドレス Chrome って Linux の CUI 環境とか画面が無いところで使ってることが多くて Chrome 全体はいらないのに全体を入れるしかなくて重たくなります
これがデメリットだったのですが ヘッドレスだけの実装のスタンドアロンバイナリとなれば軽量そうです
ですがもう開発されないバージョンなわけですし 今後の追加機能は実装されないでしょう
そうなると実際に使う選択肢には入れられなそうで残念です
KV Storage が消えてた
LocalStorage はシンプルですが 機能が少なすぎて JSON 文字列化が必要だったり 保存できる最大サイズが小さかったり 同期的な処理なのでパフォーマンスに不安があったりとデメリットもあります
代わりの IndexedDB というのもありますが こちらは複雑すぎて気軽には使いませんし あまり使われているところを見ません

その中間で良さそうなものに KV Storage というのがありました
KeyValue 形式で LocalStorage みたいに簡単に使えて内部では IndexedDB を使うというものでした
以前見たときはまだ提案段階だったので標準機能として使えなかったのですが期待していたものです

最近は全然見かけず Chrome の新機能の一覧にも出てきません
どうなったんだろうと見てみると もう開発されてないようです

https://chromestatus.com/feature/6428344899862528
https://developer.chrome.com/blog/kv-storage/

Chrome の機能ごとの詳細ページでは 「No longer pursuing」 となっていますし 紹介していた Chrome Developers のブログ記事でも利用できなくなったと書かれています
オリジントライアルまでやって その後こういう状態になったってことは使う人がほとんどいなかったとかなんでしょうか
実装されてオリジントライアルまで行ったのに こうなったってことは今後も期待はできなそうです
この機能は消えたものと考えていいかもしれませんね

リポジトリもアーカイブされています
https://github.com/WICG/kv-storage
JavaScript で使用メモリ量を調べる
devtools を使えば詳細を見れますが そこまで必要なことは少なく ページ全体の量くらいで良いことが多いです
それだとブラウザのタスクマネージャでも見れますが JavaScript 内では取れないのでしょうか

そういえば performance.memory というものがありました

performance.memory
// MemoryInfo {totalJSHeapSize: 5041902, usedJSHeapSize: 2708906, jsHeapSizeLimit: 2172649472}

しかしこれはもう deprecated らしいです
これで取得できる情報はあまり正確な値ではなかったようです
代わりに performance.measureUserAgentSpecificMemory() を使えばいいようです

Chrome ではすでに実装されてるようなのですが performance.measureUserAgentSpecificMemory は undefined でした
セキュリティの都合でクロスオリジン分離されてるページじゃないと使えないんだとか
window.crossOriginIsolated が true になっている必要があります

レスポンスの HTTP ヘッダーで指定する必要があるようです
Node.js でこんなサーバーを起動します

require("http").createServer((req, res) => {
res.setHeader("Content-Type", "text/html")
res.setHeader("Cross-Origin-Opener-Policy", "same-origin")
res.setHeader("Cross-Origin-Embedder-Policy", "require-corp")
res.end("<h1>ok</h1>")
}).listen(8000)

localhost:8000 にアクセスして確認してみます

window.crossOriginIsolated
// true

performance.measureUserAgentSpecificMemory
// ƒ measureUserAgentSpecificMemory() { [native code] }

関数がありますね
実行してみると結果はこんなでした

await performance.measureUserAgentSpecificMemory()
// {
// breakdown: Array(4)
// 0:
// attribution: []
// bytes: 15200
// types: ['DOM']
// [[Prototype]]: Object
// 1:
// attribution: []
// bytes: 776005
// types: ['Shared']
// [[Prototype]]: Object
// 2:
// attribution: [{…}]
// 0:
// scope: 'Window'
// url: 'http://localhost:8000/'
// length: 1
// [[Prototype]]: Array(0)
// bytes: 1373861
// types: ['JavaScript']
// [[Prototype]]: Object
// 3:
// attribution: []
// bytes: 0
// types: []
// [[Prototype]]: Object
// length: 4
// [[Prototype]]: Array(0)
// bytes: 2165066
// [[Prototype]]: Object
// }

DOM や JavaScript などの種類ごとに分かれた詳細な使用量と全体の使用量が見れます

このクロスオリジン分離は SharedArrayBuffer を使うときにも必要なものです
最近はこの機能は HTTPS 必須だとか あの機能はクロスオリジン分離が必要だとか 面倒になってますね
アロー関数か function か
どっち使うのかというのが話題になってるのを見ました
もうアロー関数が使えるようになって 7, 8 年も経つのにまだそんな議論あるんですね
var を使うか let/const 使うかと同じようなものだと思うのですけど

特別な理由がなければ全てアロー関数一択だと思います

昔ながらの function キーワードで作る関数は this や arguments が使えて 式ではなく文として書いた場合は巻き上げをしてくれます
これらは基本不要ですし 無い方が読みやすくもあるので 使わないならアロー関数にします
変更するつもりのない変数を const にするような感じです
わざわざ function なんて長いのを書くということは これらの機能を使いますよーと宣言してる ような気持ちで書いてます

これらの機能と言っても実際はほぼ this ですね
arguments は基本 ...args でよくって arguments が欲しいと思ったことがあるのは引数の部分で一部を受け取ってさらに全体としても使うときがあるようなところくらいです

function fn(a, b) {
// something ...

sub(...arguments)
}

ただこれって

const fn = (...args) => {
const [a, b] = args

// something ...

sub(...args)
}

でもいいです
メインの a, b 部分が目立たず 補完時の関数情報で分かりづらいかもしれませんが そこまで気にするものではないと思いますし 気になるならこういうこともできます

const fn = (a, b, ...rest) => {
const args = [a, b, ...rest]

// something ...

sub(...args)
}

これだけのためにわざわざ function を使うほどではないです

巻き上げは先にメイン部分のコードを書いて 後にそこで使う関数を書きたい場合に便利ではあるのですが 読むときにまだ読んでない部分から関数を探すことになり 上から順に読みづらいです
なので 積極的に使わないほうがいいと思ってます
アロー関数なら(同スコープなら)使用箇所より上にあるのが確実なので 順番に読んでればすでに読んでるはずですし 探しやすいです

どうしてもメイン処理を一番上に書きたいという場合でも 関数の中からだと外側スコープは下でも参照できるので

const main = () => {
sub()
}

const sub = () => {
//
}

main()

という感じでかけます
この書き方だと ときどき main という関数を呼び出すのを忘れるので こうしてたり

setTimeout(() => {
sub()
})

const sub = () => {
//
}

setTimeout の部分は sub への代入後に実行されればいいので 後から実行してくれる関数ならなんでもいいです(setImmediate や queueMicrotask など)

そんなわけで function を使うのは this のためです
this を使う関数なので扱いに気をつけてくださいね のマークみたいなものです



あとは React で使われてるからというのも見ました
あれは関数の名前をつけたいという特殊なケースです
function を使えば名前をつけられます
アロー関数だと名前はつけられないので 代入された変数やプロパティ名が名前になります

const fn1 = () => {}
fn.name
// fn1

const obj = { prop: () => {} }
obj.prop.name
// prop

export default や関数に直接渡すケースなど代入されない場合は名前がつけられません

setTimeout(() => {}, 0)
export default () => {}

通常の JavaScript では関数の名前なんて気にすることはほぼないのですが React だと特殊です
関数コンポーネントでは関数の名前がコンポーネントの名前になります
デバッグ時にコンポーネントのツリーを表示することがあり 名前があったほうがわかりやすいです
function だと名前をつけられるので アロー関数よりこちらが使われています

React の関数コンポーネントと分かれば this は使わないことがわかるので 別にいいのかもしれませんが 余計なものがないアロー関数の方が好みです
名前付きエクスポートするか 一旦変数に入れてから export default すればいいです

export const Component1 = () => {}

const Component2 = () => {}
export default Component2

もしかすると後者は minify で名前が消えるかも?
他にも同名の変数が同一スコープにあるとか 一時変数に入れる方法が使えないケースが色々あるのかもです
ただ その場合でも function にするよりはこういう感じで書きたいですね

import { withName } from "./path/to/with-name.js"

export default withName("foo", () => {})

// with-name.js
export const withName = (name, fn) => {
Object.defineProperty(fn, "name", { value: name })
return fn
}

関数の name は上書きできないプロパティなので 再定義して書き換えています
React に限れば displayName というプロパティに名前をセットしておけば name プロパティよりも優先されるので このプロパティに名前をいれるだけでもいいかもしれません

単にスコープに変数を作りたくないだけならこれでも名前をつけられます

export default { foo: () => {} }.foo

見た目がいまいちだったり 関数を通したり一手間あるので アロー関数に揃えたいというこだわりがなければ function を使う人も多いのでしょうけど 名前みたいな付加情報は将来的にはデコレーターとかアノテーションでつければいいと思うので アロー関数に変わっていきそうな部分ですね
Import Assertions が stage 2 にダウングレードしてる
Chrome 110 で配列メソッドが追加されてる事に気づいたので 久々に TC39 の proposals リポジトリを見てみました
https://github.com/tc39/proposals

先月末から今月頭にかけての更新で Stage 4 が 2 つ増えたみたいです
配列のメソッド追加とシンボルを Weak 系のキーにできるようにするというもの
シンボルはあんまり使わないので後者はどうでもいいかなーということで その他の変更を見ていると重要そうなのがありました

Import Assertions が Stage 2 にダウングレードしています
リポジトリを見に行くと web integration で問題があったとしてこの issue がリンクされていました
タイトル的に CSP で問題になるみたい
CSP なんて余計なものの都合で構文変更とかやめてほしいです

Import Assertions はもう使えるようになってから 2 年弱と長く 当たり前のように使っているのでいまさら変更は面倒が多いです
これまでのものはそのまま使える形にしてほしいですね
Bing の AI 対応
検索エンジンが AI 対応したって聞いて少し興味あったので見てみました
デザインが新しくなってて AI とのチャットっぽくなってた……けど 検索すると普通に検索結果の一覧画面が出てきました
まだテスト版なのか負荷対策なのか登録して順番待ちしないといけないみたい

それからしばらくして 紛らわしいからか検索画面は検索画面ぽい見た目に戻ってました
順番待ちしてまで使いたいほどでもないし 試すのは普通に使えるようリリースしてからでいいかな

AI と言われてネットだと過剰にすごいものぽく扱われてるけど 結局それっぽい回答してるだけで内容に信頼性があるわけじゃないですからね
以前から Google 翻訳で AI 対応して自然な文章になったけど意味が正反対になってたとかあったように それっぽいだけで正しいとは限らないので逆に紛らわしいです
学習データがネットならデマとか素人が書いた情報がいっぱいあってそれらも含まれてるわけですからね
人間相手でも 専門家でもない人に「●●について調べてきて」と頼んでもネットで見かけた情報をまとめただけで 文章としていかにもそれっぽくても内容は間違いが多い というのと一緒です

チャット相手として遊ぶには面白そうですが 正しい情報が欲しくて調べるときに使うには 普通の検索エンジンの方がいいです
Bing は検索エンジンとして質が低すぎたのでこれのほうが使い道あるかなと思いますが Google は真似せずこれまでどおりの検索エンジンを続けてほしいですね
Edge が PDF の表示に Adobe のエンジンを使うらしい
やめてほしい

Adobe 製品は入れたくないし入れてほしくない
IE の頃の使いづらい PDF 表示みたいになるようにしか思えない

せっかくウェブで快適に PDF を見れるエンジンが Chrome に搭載されてるのに Adobe のエンジンなんていれなくていいよ
なんのためにそんな余計なことをするの
無効にできるならいいけどできないなら PDF を開くために Chrome 入れるみたいなことが必要になってすごく面倒になる
PHP に組み込み関数のセーフ版を提供するライブラリがあるみたい
見かけた PHP のコードに Safe 版関数を使っているものがありました
使っているライブラリはこれのようです
https://github.com/thecodingmachine/safe

Safe 版ってなにしてるの?
文字数が長すぎるとか引数チェックをして脆弱性の対処をこのレイヤーでやってくれるの?
とか思っていたら単にエラーの場合に throw してくれるだけのようです

中身を見ても単純なものです

json_encode
https://github.com/thecodingmachine/safe/blob/v2.4.0/generated/json.php#L50

mkdir
https://github.com/thecodingmachine/safe/blob/v2.4.0/generated/filesystem.php#L1221

PHP の組み込み関数は例外を起こさず false を返して失敗を伝えるのが多いですからね

昔はその仕様に不満を感じて例外を投げてくれればいいのにと思ったこともあります
でも最近は逆で例外を投げられると catch が面倒なので return として成功と失敗を返してほしいと思ってます

JavaScript だと PHP と違いエラーが throw されるので自分でラップしてエラーでも return するようにしたりしてたりします
非同期関数だと 「.catch(error => null)」 とか 「.catch(error => ({ error }))」 で済むのに同期関数は try-catch 句が必要で const と相性が悪くてイライラしたり……

言語によっては例外というものがなく 成功/失敗という型の中に結果や原因を保持してそれを return するものもありますし PHP のこれはこれでいいものだと思います
例外が起きても ちゃんと作るなら生のエラーメッセージをそのまま使わずユーザーが見れる形のメッセージに置き換えたりといった処理が必要で catch して書き換えて 再 throw したりするなら throw はユーザーがするものとしてライブラリレイヤーでは想定されたエラーでは例外は起こさないのほうがいいのかなと思ったりです
Node.js で事前処理をした状態で REPL を開く
REPL でデータを確認したりフィルタしたり変換したりと色々したいことがあります
そこで使うデータは別のファイルを読み取って変換したデータになるのですが REPL の中で毎回その準備をするのは大変です
1 回の REPL プロセス内で必要なデータの確認まで全てやって終わりならともかく変数等をクリアしたくてプロセスを終了して再度実行はけっこうあります
あれを見たいと思うたびに REPL のプロセスを起動してデータを読み取って準備しては手間です

「↑」キーで履歴呼び出しができるのですが 1 実行ごとの呼び出しなので初回準備が何行にも及ぶと再実行が面倒です
スクリプトにまとめておいてそれを require するだけにすれば 1 行になりますが REPL 操作部分が長くなると履歴からその require を探すのも一苦労だったりします
ということでいい方法を探したところ -r オプションでモジュールをロードするのが良さそうでした

仮にデータは 1 行 1 JSON 形式で保存されたテキストファイルで それをパースしてオブジェクトの配列として扱うとします

> cat data.txt
{"x":1,"y":2}
{"x":4,"y":0}
{"x":2,"y":10}

> cat pre.js
globalThis.items = require("fs").readFileSync("./data.txt").toString()
.split("\n").filter(x => x.trim()).map(x => JSON.parse(x))

この pre.js を -r で読み込むと REPL が実行される前にロードされるのでグローバル変数にデータを入れておけば参照できます

> node -r "./pre.js"
Welcome to Node.js v18.12.1.
Type ".help" for more information.
> items.filter(item => item.x === 1)
[ { x: 1, y: 2 } ]

データのファイルが複数あり ファイルを選択できるようにしたい場合はコマンドライン引数で渡すようにもできます

> cat pre.js
globalThis.items = require("fs").readFileSync(process.argv[2]).toString()
.split("\n").filter(x => x.trim()).map(x => JSON.parse(x))

> node -r "./pre.js" - data.txt
Welcome to Node.js v18.12.1.
Type ".help" for more information.
> items
[ { x: 1, y: 2 }, { x: 4, y: 0 }, { x: 2, y: 10 } ]

プロセスを終了して再実行するときには node コマンド内の REPL で実行したコマンドは関係なくシェル側の履歴から参照するので 1 回 「↑」 を押して再実行するだけです
配列の最後を取得する
今では at メソッドがあるので

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

という風に簡単に取得できます

これが無い頃は不便でした

items[items.length - 1]

と items を 2 回参照するので一旦変数に入れる必要があります
ひとつの式の中で書きたいときに扱いづらいです
なので関数を作るのですが 関数だと配列を取得する式全体を囲む必要があるので書きづらいです
なので Array.prototype を拡張して last みたいなメソッドを追加していました
しかしこれでも 最初の取得の [0] とは非対称感があっていまいちです
わかりやすさ的には first メソッドも作ればいいのかもですが コードが長くなります
特に Python も書いていた頃だったので

[1, 2][0] # 1
[1, 2][-1] # 2

のスッキリとした書き方に憧れます
実際に使うのは「最後」のみで 後ろから N 番目なんてまずないので -1 だけ対応すればいいかとこんなことをしてみたりしました

Object.defineProperty(Array.prototype, "-1", { get: function () { return this[this.length - 1] } })
;[1, 2][-1] // 2

-2 もいけそうなのに動かないし 色々問題はあって実用はためらわれるものの結構好きな方法です

ためらった結果使わず prototype を拡張する last もなんかなぁ ということで結局はこんな形でした

[1, 2].slice(-1)[0]

でもこれをみては [-1] って書けるようにしたいなぁと

at が使えるようになっても 最初は [0] なのに最後は .at(-1) になる非対称感のイマイチさを感じてますし かといって 最初や 2 番目の取得でわざわざ at を使おうとは思いません
最後を取得したいってことがあると [-1] で取得できるようにしたいなーと思っては諦めてる状況です
sticky は背景グラデーションで綺麗に表示できない
見出し (sticky)

本文
本文
本文

見出し (sticky)

本文
本文
本文

の繰り返しがあるとき コンテナ要素の背景がグラデーションになってると 綺麗な表示にできない
sticky で上に張り付いているとき 非 sticky 要素は sticky 要素と重なってスクロールしていく
背景が透明だと重なって見えてしまうので そう見えないように通常は sticky 要素に背景色を指定する

sticky 要素が背景のグラデーションを無視した固定色なら単純に background-color 指定するだけ
でも背景のグラデーションを維持したい場合はスクロール位置に応じて背景色を後ろに合わせたグラデーションに揃えることになって現実的じゃない
透明にするのじゃなくて 裏側の色をコピーしてくる機能がほしい
複数の sticky 要素は重なってる
見出し (sticky)

本文
本文
本文

見出し (sticky)

本文
本文
本文

の繰り返しがあるとき 下にスクロールして 3 つ目の見出しがついてきてる状態では 3 つ目だけじゃなくて 1 つ目と 2 つ目もついてきてる
それらが重なっていて 一番上に 3 つ目があるから 3 つ目だけがついてきてるように見えるだけ

これは同じ高さならいいけど 2 つ目の見出しが長くて折り返しが入って高さが大きくなると 3 つ目がついてきてるときに 2 つ目のはみ出た分が見えてしまう

<style>
.container {
overflow: auto;
width: 200px;
height: 200px;
}
.head {
position: sticky;
top: 0;
color: white;
}
.body {
height: 300px;
}
.red {
background: red;
}
.blue {
background: blue;
}
.green {
background: green;
}
</style>

<div class="container">
<div class="head red">1</div>
<div class="body">body</div>
<div class="head blue">2<br/>2-2</div>
<div class="body">body</div>
<div class="head green">3</div>
<div class="body">body</div>
</div>

固定中の要素の前の固定中の要素は非表示にできるといいけど 現状の CSS ではそういうことはできないので 高さを揃えるか JavaScript でがんばるしかなさそう



[追記]

フラットにせず sticky にしたいものを区切りにして div で囲めれるなら囲むだけでそれだけで対処できた

<style>
.container {
overflow: auto;
width: 200px;
height: 200px;
}
.head {
position: sticky;
top: 0;
color: white;
}
.body {
height: 300px;
}
.red {
background: red;
}
.blue {
background: blue;
}
.green {
background: green;
}
</style>

<div class="container">
<div>
<div class="head red">1</div>
<div class="body">body</div>
</div>
<div>
<div class="head blue">2<br/>2-2</div>
<div class="body">body</div>
</div>
<div>
<div class="head green">3</div>
<div class="body">body</div>
</div>
</div>

sticky でついてくる状態は内側 div の範囲になる
直後の 「body」 が表示されてる間はついてくるけど内側 div がスクロールされ画面外にいくと ついてこなくなる
sticky と次の sticky が重なるとき 上に乗るのではなく 上にスクロールされていくので自然な動きになる
react-hook-form の感想
便利そうかもと思ったらやっぱり違うなと思ったりで ちゃんと使うことはなかったライブラリですが 使われてるコードをいじる機会があったので色々使ってみた感想です
簡単に言うと React ぽくなくなったり 単純なフォームじゃなくなると useEffect だらけで辛くなるので そんなに積極的に使いたいものではないです

react-hook-form を使うと 内部で state を管理してくれて 再レンダリングを防げます
各 input が props ではなくコンテキスト内の form から値を取得するので 親の再レンダリングが不要になり 変更があった input のコンポーネント内だけが再レンダリングされます
表示内容が多いページで memo なしだと入力ごとに再レンダリングされる範囲が広くなり 重たくなったりしますが それを防げます

フォームの制御が特になく 項目が多いだけのフォームだと楽に使えて便利です
しかし 入力値の変化に応じてあちこちを変更する場合は面倒になってきます

useEffect(() => {
const subscription = watch((values, { name, type }) => {
if (name === "foo") {
setValue("bar", values.foo)
} else if (name === "bar") {
setState(values.bar)
}
})
return () => subscription .unsubscribe()
}, [watch])

みたいに callback を指定する watch を使い form の値や state を更新していくことになります
useEffect が必要です

更新された場所の名前はわかりますが 更新前の値はわかりません
変化した内容に応じて処理を変えたいなら自分で前の値を保持する仕組みを作っておかないといけません

また イベントハンドラ的な部分での処理ではなく render 関数中の処理で現在の form の値を使いたいなら 名前指定で watch を使います

const [foo, bar] = watch(["foo", "bar"])

この場合はどれが変わったのかわかりません
また 変わるたびに この watch を使ったコンポーネントが再レンダリングされるので useState に入れているのと変わらなくなります
ものによっては form の値のほとんどを watch に入れることになり useState と対して変わらないってこともあります

watch で取得した値を使うのが hook だと仕方ないですが JSX 部分なら input 以外でも form を参照するようにしてしまうのもありです
input のように controller を使って値を取得すれば watch にする必要がなく コンポーネント全体の再レンダリングを減らせます

input に使う以外の form とは関係ない値でも form に入れてしまい state にせず form で管理するのもありかもです
そうすれば form の処理だけに統一できますし 名前指定の watch を減らせます
それに コンポーネント間の共有でコンテキストのようにも使えます
親コンポーネントで form に関数を入れて 深い部分のコンポーネントでそれを参照します
ただしあくまでライブラリとしては form なのでこの使い方はいいのだろうか?という気はします
想定されたことを外れると将来的なアップデートで面倒な目に合うのは十分考えられます
そういう使い方のものを別に用意したほうがいいのかもという気はしてます

深い部分で watch を使う注意点ですが 使ったコンポーネントだけが再レンダリングされそうに見えてしまいますがそうではないです
React の仕組み的にそういう手段はなく useForm を使ったコンポーネントが再レンダリングされます
その結果 その子孫コンポーネントも再レンダリングされてそうみえるだけです

こういうことをしてみるとわかりやすいです

const Input = ({ control, name }) => {
const { field } = useController({ control, name })
return <input {...field} />
}

const Child = ({ control, watch, name }) => {
const value = watch(name)
console.log("render Child", name)
return (
<div>
<div>input value: {value}</div>
<Input control={control} name={name} />
</div>
)
}

const MemoChild = React.memo(Child)

const Parent = () => {
const { watch, control } = useForm({ defaultValues: { a: "1", b: "2", c: "3" } })
console.log("render Parent")
return (
<div>
<Input control={control} name="a" />
<hr />
<Child control={control} name="b" watch={watch} />
<hr />
<MemoChild control={control} name="c" watch={watch} />
</div>
)
}

Parent で useForm して watch を Child に渡します
Child で watch していて その input を書き換えると 「render Parent」 もログに表示されます
Parent コンポーネントも再レンダリングされています
また Child をメモした版の MemoChild を使うと props の control, name, watch には変更がないため MemoChild コンポーネントは再レンダリングされません
結果 Parent は再レンダリングされるのに MemoChild は再レンダリングされず 「render Child c」 はログに表示されません
画面上でも input の現在の状態を表示しているのですが ここが更新されず初期状態のままです

また props や state を見て ちょっと加工するというだけでも useEffect で setValue が必要になります
普通の React なら render 関数内で計算したり JSX 内に式を入れるくらいで済むことのために useEffect が増えていくのは見通しが悪くなるので不満です

さらに問題になるのはあちこちで同じ値を更新したいときです
useEffect やイベントハンドラの中で setValue することになりますが 特に useEffect だと呼び出し順がわかりづらいです
あちこちのコンポーネントから setValue されるとどういう順で呼び出されて値が変わるのかデバッグしづらく 思い通り動かないことがけっこうありました
通常の React なら useEffect は極力なくせて render 関数内での処理です
親から子の順ですし レンダリングごとにイミュータブルのはずなので 困ることは少ないです

完全に独立した入力項目で 初期値以外は外部から変更されることがないような場合はシンプルに書けて良さそうですが 制御する部分が多いと不満点が目立ちます
やっぱり 素の React で state 管理してる方が見やすくていい気がしますね
React で hook はコンポーネントを分けたい
React の hook は便利なんだけど 作るものが大きく複雑になってくるとコンポーネントも複雑になってくる
やっぱり props として受け取った状態から HTML を作るだけ のコンポーネントのほうがスッキリしていて好き

そうするためにコンポーネントを 2 段にして外側では hook を使ってデータを取得したり加工したりするだけで そのデータを内側コンポーネントの props として渡す
内側コンポーネントは hook は使わず 受け取った props から画面を作る JSX を作って返す

スッキリはするけど長くなるのが面倒
それにこの感じ Redux が流行ってた頃に見かけた作り方に近そう
自分でそういうのは作ってないのであまり詳しくはないけど プレゼンテーションコンポーネント?みたいな名前がついてて ストアと通信してデータを受け取る部分と 副作用を持たない props から JSX を作るだけのコンポーネントに分けていたと思う
hook になってもそれでいいのかな と思ったけど こう分けるのは今では推奨しない みたいなのも見かけた
直接 hook を使えばいいから らしいけど使うとコンポーネントがごちゃごちゃしてくるし テストもしづらくなる

useMemo や useCallback はパフォーマンスの都合なもので なくても動作は変わらないからどっちでもいいけど useEffect は入るとかなり複雑化するから コンポーネント外に持っていきたい
useState は関数の引数として状態と更新関数を受け取るのと同じと聞くこともあるけど リセットしたいときとか テスト時のダミーデータを渡すことを考えたらやっぱり違うかなって思う

WebComponents だと標準の HTML 要素みたいなものとして考えればいいので内部で状態を持って外部通信もしてっていうのはありだと思う
img タグとか画像を取得して表示してるし
でも React の考え方だと 親が子のメソッド呼び出したり 自由に子の状態を取り出したりしない
親で最初から持っていて子に渡すだけ
その考え方だと コンポーネント内で外部通信とか状態を保持とか色々しないほうがいいようにも思う

とりあえず 外部通信したりアプリ固有のロジックでデータを変換したり計算したりみたいのをコンポーネントの中じゃなくて外でやってコンポーネントはその結果を受け取るだけにしたい
1..x で 1 から始まる連番を作る
なんとなく思いつきで作ったもの
1..x で 1 から始まる連番のイテレータを作る
終わりはなく無限に続く

for (const n of 1..x) {
if (n > 5) break
console.log(n)
}
// 1
// 2
// 3
// 4
// 5

1 以外からでも始めれる

const it = 123..x
it.next()
// {value: 123, done: false}
it.next()
// {value: 124, done: false}
it.next()
// {value: 125, done: false}

見た目はそれっぽいけど 中身はこんなのなので実用性はない

Object.defineProperty(Number.prototype, "x", { get: function* () { for (let i = +this; ; i++) yield i } })
Node.js でモジュールをフォルダにまとめるとき
(1) foo.js を読み込んで foo.js が foo/ 以下のモジュールを読み込む
foo.js
foo/file1.js
foo/file2.js

(2) foo/index.js を読み込んで foo/index.js が foo/ 以下のモジュールを読み込む
foo/index.js
foo/file1.js
foo/file2.js

CJS なら (2) が相性がいい
フォルダ名の foo を require すれば自動で index.js を補完してくれる
もとは foo.js 単体であとからモジュール分けするときに foo で require していれば読み込む側のコードは変更しなくていい
それに foo に関係するモジュールはすべて foo フォルダに入ってる

だけど ESM は CJS みたいな補完はなくて完全なファイル名が必要
どのモジュールをロードしているのかわかりやすくはあるけど import に foo/index.js のように書かないといけなくなる
foo.js からモジュール分けすると使うところの変更も必要
だから ESM なら (1) 形式にしたほうがいいのかなと最近思ってたりする
foo.js が foo/ を単純にインポートしてエクスポートするだけならまだいいけどここでも処理を入れると foo フォルダに入っていて欲しくなる
PostgreSQL でデータベース・ロール固有の設定を表示する
alter database や alter role で設定した内容を表示したいです
show を使えば現在有効な設定を確認できますが alter database などで変更したものかはわかりません

pg_db_role_setting を見ればその情報が取れます
https://www.postgresql.jp/document/14/html/catalog-pg-db-role-setting.html

データベースとロールを用意します

postgres=# create database db1;
CREATE DATABASE
postgres=# create role foo;
CREATE ROLE

データベースとロールの設定を変更します

postgres=# alter database db1 set timezone to 'asia/tokyo';
ALTER DATABASE
postgres=# alter role foo set search_path to a, b, c;
ALTER ROLE
postgres=# alter role foo in database db1 set search_path to public;
ALTER ROLE

一応変更されてることの確認です

postgres=# show timezone;
TimeZone
----------
Etc/UTC
(1 row)

postgres=# \c db1
You are now connected to database "db1" as user "postgres".
db1=# show timezone;
TimeZone
------------
Asia/Tokyo
(1 row)

pg_db_role_setting の中身を表示します

db1=# select * from pg_db_role_setting;
setdatabase | setrole | setconfig
-------------+---------+-------------------------
16388 | 0 | {TimeZone=asia/tokyo}
0 | 16391 | {"search_path=a, b, c"}
16388 | 16391 | {search_path=public}
(3 rows)

設定した内容が表示されています
片方のみだと未設定の部分は 0 です
両方の組み合わせに対して設定すると両方に oid が入ります
setdatabase と setrole の数値は pg_database の oid と pg_roles の oid になっています

db1=# select oid, datname from pg_database;
oid | datname
-------+-----------
5 | postgres
16388 | db1
1 | template1
4 | template0
(4 rows)

db1=# select oid, rolname from pg_roles;
oid | rolname
-------+---------------------------
6171 | pg_database_owner
6181 | pg_read_all_data
6182 | pg_write_all_data
3373 | pg_monitor
3374 | pg_read_all_settings
3375 | pg_read_all_stats
3377 | pg_stat_scan_tables
4569 | pg_read_server_files
4570 | pg_write_server_files
4571 | pg_execute_server_program
4200 | pg_signal_backend
4544 | pg_checkpoint
10 | postgres
16391 | foo
(14 rows)
非表示要素を検索時にみつかったら表示させる
hidden 属性に until-found を指定

<div>foo</div>
<div hidden="until-found">bar</div>
<div>baz</div>

bar は表示されないけど Ctrl-F で bar を検索すると見つかり表示されるようになる
hidden="until-found" があるとスタイルに

content-visibility: hidden;

が設定されて非表示になってる
検索で見つかると 要素から属性が消えるのでスタイルも消えて表示される

display: none ではないので 非表示の挙動が少し違う
flex/grid の子要素だと要素があるように扱われる
中のサイズは計算されないけど その要素自身のスタイルの width/height は反映される
margin/padding も反映されて余白ができる

これを避けるために別のスタイルで display: none をつけると完全にないものとして扱われるので 検索対象からも外れてみつからなくなる
これまでの hidden 属性で 属性だけつけてもクラスのスタイルが優先されて表示されることがあるので

[hidden] {
display: none !important;
}

をつけて回避することがあったけど これをやると until-found が無意味になってしまう

非表示時のサイズを指定したい場合は contain-intrinsic-size を使える

div {
contain-intrinsic-size: 100px 50px;
}

表示されたら hidden 属性が消えるので

[hidden="until-found"] {
width: 100px;
height: 50px;
}

でも良い
Microsoft のサイトのドキュメントのドメインがまた変わってる
Microsoft のドキュメントのページの URL が変わってることに気づきました
昔の見づらい頃から少し見やすくなった頃に docs というドメインになってました
https://docs.microsoft.com

これがいつの間にか learn に変わっていて docs でアクセスしたときに こっちにリダイレクトされるようになってました
https://learn.microsoft.com

C# やオフィスや fluent ui や WSL など Microsoft 製のもの全般で変わってます
ドキュメントなら docs でいい気もしますし 頻繁に変えるのはやめてほしいですね

過去に保存していた microsoft.com ドメインのリンクを確認していると support.microsoft.com でも一部 learn.microsoft.com にリダイレクトされてるのがありました
ESM のみのパッケージが不便
最近では ESM 版しか用意せず CJS 版がないパッケージもでてきているようです
CJS からでも import() を使えば ESM モジュールを読み込めますが 非同期処理になります
CJS ではトップレベルの await ができないので ESM のみのパッケージが入るだけであちこちで非同期処理が発生してとても使いづらいです

中にはこれまで CJS にしていたのにアップデートで ESM のみになるパッケージもあります
あるパッケージの最新の機能を使いたいのでアップデートしたら とても困ったことになりました
パッケージの読み込み等はすべて同期処理を前提として作っているものだったので まともに対応するなら大幅な作り直しが必要になります
まともに対応なんてやってられないので別の方法で回避することにしました

一つの方法は CJS 化すること
これまで CJS だったのですから こっちで CJS に変換してから使います
Rollup などを使います
ただこれには問題があって 対象が依存パッケージにも及びます

「MyPackage → A → B」 のような依存関係になっているとき A が ESM のみだと B も ESM のみの可能性があります
一つのプロジェクトでパッケージを分割してるような場合だと A の依存関係にも ESM のみパッケージが含まれるケースが多いです
自分のパッケージからの直接の依存関係である A だけを CJS 化しても A から B をインポートするときに require だとインポートできないというエラーになります
ESM パッケージすべての変換が必要ですが 多くなってくると 1 つ 1 つを CJS 化するのは大変です

手抜きをするなら A の CJS 変換時に依存関係も解決してバンドルすることです
A の変換済み CJS モジュールには A も B も含みます
楽にはなりますが B のパッケージが複数の A からインポートされる場合に A ごとに異なる B が存在することになります
同じパッケージを別パッケージとして複数回読み込むことになるのでムダが多いです
さらにキャッシュなどで状態を持つモジュールがあると 別モジュールとみなされることで動作に影響する場合もあります
class なら instanceof を使った判定を行うケースもあり 別モジュールだと実体が違うので別物とみなされ意図した動作にならない場合もあります
この点ではパッケージ 1 つ 1 つを CJS 化した方が良いです


別の方法では CJS 化せずそのまま使います
問題は import() によって読み込みが非同期化することなので 事前にすべての読み込みを終えてからメインの処理を実行することで非同期読み込みを不要にします

エントリポイントのスクリプトが index.js なら boot.js を追加してエントリポイントをこっちに変更します
boot.js はこんな感じにします

const esm_modules = require("./esm_modules.js")

!async function() {
const modules = [
"mod1",
"mod2",
// ...
]

const loaded = await Promise.all(modules.map(name => import(name)))

for (const [idx, name] of modules.entries()) {
esm_modules[name] = loaded[idx]
}

require("./index.js")
}()

esm_modules という空のモジュールを用意して ここを import 済み ESM モジュール置き場にします
ESM のパッケージをすべて ここで import() します
読み込みが終われば esm_modules のプロパティに保持します
これらの処理が終わってから require で本来のエントリポイントの index.js を実行します

その他モジュールでは ESM のみパッケージを require するところを esm_modules の require に置き換えます

const { mod1 } = require("./esm_modules.js")

index.js 以降の処理では ESM のみパッケージは変数に保持されているので非同期処理が不要になります
多少のコードの修正は必要ですが 非同期対応に比べれば遥かに簡単な修正で済みます
Parcel2 まだ問題多めな気がする
以前 Parcel1 を使ってたものを Parcel2 に移行しました
大きな問題はなさそうで 高速になって良くなったと思っていたのですが 使っているとまだ問題がありました

◯ ファイル名のハッシュ値が安定しない

ビルドするとファイル名にハッシュ値が付きますが 同じソースから複数回ビルドするとハッシュ値が変わることがあります
リリースノートによれば 2.7 で修正されたらしいのですが最新の 2.8.2 でも発生しています

複数の .js ファイルからインポートされる .js が .css をインポートして その .css の中で url(); で画像をロードしています
このときの画像ファイルで発生しています
ただ この CSS ファイルがロードする画像ファイルは 10 を超えるのですが 1 つでだけ発生しています

高速化を重視しているので並列化した結果どっちが先に処理されるか次第という感じなのかもしれません

ビルド結果をバージョン管理等に含める場合 これらも差分として扱われるので不便です

◯ CSS の順番が import 順にならない

CSS Modules を使っているのですが 出力される CSS の順番が期待どおりになりません

import foo from "./foo.css"
import bar from "./bar.css"

と書いた場合は foo が先で bar が後であることを期待します
しかし bar の方が先にくるので優先順位が想定どおりにならず表示が崩れました
これは 2.8 にアップデートしてから発生しています
依存関係やバンドル関係のアルゴリズムを変更したらしいので多分これが原因でしょうね

CSS をモジュール化しても複数のモジュールのクラスを 1 つの要素につけることがあるので順番は考慮してほしいものです
汎用的な共通スタイル部分を先にインポートしてそのモジュール固有のスタイルを後にロードしても共通スタイルが優先されたりします

これの対処のためにモジュール固有スタイルはすべてに不要なセレクタをつけて詳細度を上げるなんてことをしないといけなくなりました
2023 年
新年あけましておめでとうございます

去年はメインの方のブログは記事少なめで こっちの Short が多めだったと思います
ムダに長文になっても後から探すときに自分でも面倒なのでこんな感じでいいかなと思ってます

ただ 去年はあんまり新しいものには触れてなかったなーという気はしています
いつものほぼ変わりがない感じです
ブラウザ周りでは React の使用率が一番高くなってた気がします
すでに別記事で書いてますが それ故に不満度も上がってなにか別のにしたいとも思ってます

そもそも JavaScript 界隈が最近はあまり変化してない気がしますね
最近は新機能があまり入っていませんし 特に ES2015 や async/await みたいな構文に大きな変化がある提案はずっと Stage1, 2 のままです
Stage3 では期待しているものもありますが まだブラウザには実装されていません
ECMAScript 以外のブラウザ機能もあまりこれといったのがないです

見かける話題も JavaScript やブラウザ機能というよりはサードパーティライブラリがほとんどです
特に TypeScript による侵食がひどすぎて ライブラリや記事を見るときに TypeScript が多すぎて嫌になります
あとは SSR 前提フレームワークだったり クラウドというか CDN 関係の話とかあまり自分とは関係なく興味ないのが多めです

いっそのこと他言語始めて見るのもありかなーと思ってたりもします
TypeScript のおかげで再確認できたのはやっぱり動的言語は良いです
ただ JavaScript/PHP/Python と有名どころはすでに使っています
他だと Ruby/Perl/Lua/PowerShell 等でしょうか

PowerShell は一応使ってると言っていいのかもしれません
モジュール化とか高度な機能は使ってないですが ちょっとした bat ファイル代わりのスクリプト程度には使っています
Lua は昔軽く使ってみましたが 使うところがなさすぎて全然使っていません
以前はプラグイン用言語として使われているのを見ることがあったのですが 今だと JavaScript などのほうが多い気がします
Perl は 6 が raku に改名したり 5 の延長である 7 を出す予定とか以前言っていたのに未だに出てなかったりでよくわからない状態になってます
過去に使ってた人ならともかく新規に使い始めるような言語ではない気もします
Ruby も似たようなものかもですが Perl よりはマシな気はしています
ただ最近だと PHP, Python に比べると人気は劣ってる気はしますし RoR もあまり聞かなくなりましたし デフォルトでインストールされてるものでもないので使い所があまりない気がしています
そんな感じで結局去年はこの辺は触れてきてないんですよね
やっぱりそれを使って作りたいものがあってこそでしょうね

という点だと Rust でしょうか
Electron アプリを年に 1 回あるかどうかくらいで更新しているのですが 破壊的変更が多すぎて対応が面倒です
Electron みたいな特殊なことをせずシンプルに OS の WebView を使うらしい Tauri だとこの辺は楽になったりしないかなと期待していつか移行してみようと思ってます
ただ WebView だけで完結しないので ファイルアクセスなどのバックエンド部分は Rust で書かないといけないみたいです

あとは Flutter です
Windows 対応もしたとかでここ最近では目にする機会が増えてます
最近は Windows 開発でも WinUI みたいな新しいのがあるようですが WinForms にしても WPF にしても使いやすいとはいえないものだったので そこまで期待してないです
こういう別手段があるならそっちを試してみたいなというところです
それに以前は不満に感じていた Dart ですが TypeScript を触れてみるともしかしていいものでは?と考え始めました
TypeScript は JavaScript の互換性にこだわるあまり中途半端な微妙なものになっています
そもそも JavaScript は静的型付け言語として作られてないのですから 型をつけるならそれに合わせて変えるべき部分はありますが JavaScript のスーパーセットであることにこだわっているので不足や制限が多くなり不満点が目立ちます
特にウェブ互換性のために残っているおかしな仕様などはなくしてしまうなどしてほしいのにそれができていません
Dart の場合は JavaScript を改善するのが目的で JavaScript に似ていますが TypeScript ほど互換性重視はしていません
変数宣言や引数・返り値の型の書き方が C に寄りすぎていて この点では TypeScript の方が好みということもあり あまり細かいところは見ずに全体的なコードの構文を見て Dart は使おうとは思わない と思っていました
ですが TypeScript で多くの不満を感じたあとなら良いところが多く感じられるかもと期待しています
世間的には Dart より TypeScript の方が優れていると言ってる人が多そうですが 本当にそうであれば Flutter の言語を TypeScript に切り替えるとかいう話が出ても良さそうなのに Dart3 という新しいバージョンを出す予定というニュースも見かけましたし TypeScript とは別の方向の言語として進んでいくのでしょう
あとこれまで不満に感じていた UI の記法ですが XML より JSON の方がいいという点では HTML に合わせなくてもいいのではということで JSX 的なものがないのは別にいいのかなと感じているので 今年使ってみようかな度が一番高いものかもしれません
AlmaLinux9 は普通に Python が入ってる
CentOS8 では Python は初期状態では入ってませんでした
一応システム内部用としてはあるのですがパスは通っていなくて 使うことは推奨されない形でした
直接 /usr/libexec/platform-python を実行すれば使えます
🔗 CentOS8 の Python の場所

これは AlmaLinux8 でも同じです
AlmaLinux9 でもここは変わらないかなと思っていたのですが python3 コマンドを実行したら普通に動きました

調べてみると platform-python は同じ場所にあるのですが /usr/bin/python3.9 へのシンボリックリンクになっていました

[AlmaLinux9]
[root@061e426ad383 opt]# ls -l /usr/libexec/ | grep platform
lrwxrwxrwx 1 root root 18 Apr 16 2022 platform-python -> /usr/bin/python3.9
lrwxrwxrwx 1 root root 18 Apr 16 2022 platform-python3.9 -> /usr/bin/python3.9

/usr/bin/python3.9 はバイナリの実行ファイルです

AlmaLinux8 ではこうなってました

[AlmaLinux8]
[root@4394942894ff /]# ls -l /usr/libexec/ | grep platform
lrwxrwxrwx 1 root root 20 Apr 29 2022 platform-python -> ./platform-python3.6
-rwxr-xr-x 2 root root 11864 Apr 29 2022 platform-python3.6
-rwxr-xr-x 2 root root 11864 Apr 29 2022 platform-python3.6m

platform-python3.6 や platform-python3.6m がバイナリの実行ファイルです

dnf の shebang を見てみると AlmaLinux9 では platform-python ではなく直接 python3 を指定してました

[wsl@LAPTOP-W10:~]$ sudo podman run --rm almalinux:8 head -n 1 /usr/bin/dnf
#!/usr/libexec/platform-python
[wsl@LAPTOP-W10:~]$ sudo podman run --rm almalinux:9 head -n 1 /usr/bin/dnf
#!/usr/bin/python3

どういう経緯があったのかはわかりませんが不評だったので元に戻したのでしょうか
VSCode で上下を簡単に揃える
ソースコード上で = や : の上下位置を揃えようとする人がいます
個人的にはムダでしかないので不要だと思いますし フォーマッターがやるにしても差分が増えてしまうのでやってほしくないものです

しかし ものによっては視覚的に縦に揃えて見たい ということもあります
例としてはこういう感じで揃えて見たいです

const one = 100001
const two = 100002
const three = 100003
const four = 100004
const five = 100005
const six = 100006
const seven = 100007
const eight = 100008
const nine = 100009
const ten = 100010
const eleven = 100011
const twelve = 100012
const thirteen = 100013
const fourteen = 100014
const fifteen = 100015
const sixteen = 100016
const seventeen = 100017
const eighteen = 100018
const nineteen = 100019
const twenty = 100020



const one       = 100001
const two = 100002
const three = 100003
const four = 100004
const five = 100005
const six = 100006
const seven = 100007
const eight = 100008
const nine = 100009
const ten = 100010
const eleven = 100011
const twelve = 100012
const thirteen = 100013
const fourteen = 100014
const fifteen = 100015
const sixteen = 100016
const seventeen = 100017
const eighteen = 100018
const nineteen = 100019
const twenty = 100020

対応してるフォーマッターがあればいいですが こういうことをするのは少数だと思います
それに準備が面倒です
たまにしか使わないので 件数が少ないなら手作業でしたほうが早いです

とは言え ある程度の件数になると手作業でやるのも嫌になってきます
方法のひとつは / *= */ を \t に置換してエクセルにコピペです
ソースコード自体が揃う必要はなく確認用なのでこれでもいいです

しかしエクセルって結構開くのに時間がかかりますし 入れてない PC も多いです
で いい感じにできないものかと考えていたら VSCode 標準機能だけで工夫すれば簡単にできました
エクセルを起動する間に終わってしまいます

こういうデータがあるとします

const obj = {
short1: "a",
middle1: "b",
longlong1: "c",
short2: "A",
middle2: "B",
longlong2: "C",
}

マルチカーソルで全部の「:」を選択して 右にスペースを多めに確保します
最初の「:」を選択して Ctrl-D を繰り返し押せば次の「:」も追加で選択できます

const obj = {
short1: "a",
middle1: "b",
longlong1: "c",
short2: "A",
middle2: "B",
longlong2: "C",
}

こんな感じで一番長いキーの「:」よりも一番短いキーのバリュー位置が右になったら十分です

次はスペースを幅 0 の矩形選択で縦に選択します
「|」が選択位置を表しています
マウスのミドルクリックのドラッグや Ctrl-Shift-Alt-←↑↓→ で矩形選択できます

const obj = {
short1: | "a",
middle1: | "b",
longlong1: | "c",
short2: | "A",
middle2: | "B",
longlong2: | "C",
}

Ctrl-Shift-→ で各行を次の区切りまで範囲選択します
その後に Shift-← や Ctrl-Shift-← などでスペースのみを選択するように調整してから選択範囲を削除します
こうなりました

const obj = {
short1: "a",
middle1: "b",
longlong1: "c",
short2: "A",
middle2: "B",
longlong2: "C",
}

揃ってますね
慣れるとササッと操作できるので置換してエクセルを起動してる間には VSCode 上で揃えてしまえます
PHP で名前付き引数が使えるようになってた
久々に PHP を使ったら名前付き引数が使えるようになってた (8.0 かららしい)

function fun($foo = 'FOO', $bar = 'BAR') {
echo "foo is $foo\n";
echo "bar is $bar\n";
}

fun(bar:"b");
foo is FOO
bar is b

PHP の関数って引数が多いのが多いし 引数だけみてこれがなんの値かわからないのが多かったからよさそう

fn1(1, 2, false, 'a', 3, true);

みたいなコードでそれぞれの引数が何を表してるのかなんて全然わからない

良さそうな機能ではあるんだけど 名前付き引数のみに制限はできないみたいなので 作る側が名前付き引数で使ってくれることを期待しても使う側が面倒だからと↑みたいコードで書くことは有り得そう
名前付き引数前提で考えてると引数の順番をそれほど考慮してないことも多くなりそうだし 名前の指定が必須ってできたほうがいいと思う

JavaScript だとオブジェクトで渡すフォーマットにしてその点を解決してるから PHP でもこれまでみたいに連想配列で受け取るほうがいいのかも

Python は構文として引数が positional のみか keyword のみかどっちでもありかを表現できるからいいんだけど
これを真似してくれないかな

def fn(foo, /, bar, *, baz):
print(foo, bar, baz)

foo は positional のみで keyword では渡せない
bar は positional でも keyword でも渡せる
baz は keyword のみで positional では渡せない

fn(1, 2, baz=3) # 1 2 3
fn(1, bar=2, baz=3) # 1 2 3

fn(foo=1, bar=2, baz=3) # error
fn(1, 2, 3) # error
Edge で履歴一覧を取得する
標準の履歴画面だとエクスポート機能は無い
検索はあるけど 「http://」 で http のサイト一覧が出ることはなく全件ヒットみたいな動き
単純に URL に入力内容が含まれるかではなく色々内部でやってそう
一応クオートで囲めばそれっぽくはなったけど 生のデータをもとに自分でフィルタ処理をしたい

履歴画面で devtools を開いてみたけど localStorage とかには保存されてない
ソースコードを見ると chrome.send で Edge 側で用意した非公開 API を呼び出してそう

これを無理に使うよりは拡張機能の機能で取得したほうが楽そうなので拡張機能を使う

適当なフォルダに manifest.json を準備

{
"manifest_version": 3,
"name": "popup",
"version": "1.0",
"action": {
"default_popup": "popup.html"
},
"permissions": ["history"]
}

同じフォルダに popup.html もつくる

<h1>popup</h1>

「edge://extensions/」 を開いて 開発者モードを有効にして このフォルダを選択
ボタンが追加されてるのでそれをクリックしてポップアップウィンドウを開く
ポップアップウィンドウで devtools を開く
あとは この devtools のコンソールで

const items = await chrome.history.search({ startTime: 0, maxResults: 10000, text: "" })

items.filter(item => item.url.includes("http://"))
.map(item => `${new Date(item.lastVisitTime).toLocaleString()} ${item.url} ${item.title}`)

みたいな感じで好きにフィルタできる
text は必須なので空文字を入れて絞り込みせずに持ってきて 配列の filter メソッドで処理してる

JavaScript で自由に処理できるので CSV 形式で保存なども簡単にできる
エクスプローラーのサイドバーに 「Linux」 が増えてた
いつのまにかエクスプローラーのサイドバーに 「Linux」 という項目が増えてました
普段 「クイックアクセス」 や 「PC」 の部分が見えていて 下の方にスクロールしないので全然気づいてませんでした

この 「Linux」 を開くと WSL のディストリビューション一覧が共有という形で見れます
ディストリビューションを選ぶとそのディストリビューションのルートディレクトリが表示されます

これまでアドレスバーに 「\\wsl$」 と入力してから開いていた場所と同じような動きです
「Linux」 からフォルダを開いてからアドレスバーでパスを取得するとこうなってました

\\wsl.localhost\Ubuntu-22.04

wsl.localhost というサーバー名になってるようです
自分で 「\\wsl$」 と入力したり そこへのショートカットをつくる手間が省けるので便利です
Edge のプライベートモード中のコピーは履歴に残らない
Edge でプライベートウィンドウを使ってその中でテキストをコピーすると クリップボードにはコピーされるのですが Win-V で使えるクリップボードの履歴には残りません
プライベートウィンドウなので気を使ってそういうことをしてるのでしょうが 余計な機能で迷惑です
履歴に残ってるからとプライベートウィンドウを閉じてから残ってないことに気づきました……

Chrome ならそんな余計なことはしないのですけどね

ググるとこの事象でバグ報告してる人がいました
ただ回避方法は見当たらないので Chrome を使うしか無いようです

Win-V のクリップボード履歴は標準で使えるのがメリットで 機能的にはかなり不足してると思うので ブラウザを Chrome にするよりも別のクリップボード管理ツールを入れるほうがいいかもしれません
setState の実行タイミング
ライブラリがちゃんと動いてくれなくて 内部挙動調べるために console.log すると実行順がよくわからないことになってて React だけでの動きを調べたメモ
コールバックタイプの setState を使って 同期的に複数回呼び出されていたので それだけの動きを確認するためにこんな感じのページを用意

const App = () => {
const [state, setState] = useState(0)

const onClick = () => {
console.log("ONCLICK")

console.log("pre setState(1)")
setState(prev => {
console.log("setState(1)")
return 1
})

console.log("pre setState(2)")
setState(prev => {
console.log("setState(2)")
return 2
})

console.log("pre setState(3)")
setState(prev => {
console.log("setState(3)")
return 3
})
}

return <button onClick={onClick}>{state}</button>
}

この画面を開いてボタンをクリックしたときのログは

ONCLICK
pre setState(1)
setState(1)
pre setState(2)
pre setState(3)
setState(2)
setState(3)
setState(2) ///
setState(3) ///

/// を書いてる行は React の devtools を入れてると薄く表示されるログ

もう一度ボタンを押すと少し変わって

ONCLICK
pre setState(1)
pre setState(2)
pre setState(3)
setState(1)
setState(2)
setState(3)
setState(1) ///
setState(2) ///
setState(3) ///

最初の 1 を見ると同期的に実行されてるようにみえるけど 2, 3 を見るとコールバックは即時呼び出されずあとになって呼び出されてるのがわかる
薄く表示されるのは Strict モードで 2 回レンダー関数が呼び出されるときに 2 回目の実行中に呼び出されたものだったと思うけど そんなタイミングで実行されてるの?
コンポーネントの関数の最初に console.log を入れて試してみると本当にそんなタイミングで呼び出されてた

二回目のボタンでは setState の 1 より先に 2 や 3 の pre が呼び出されているのも気になる
どっち先かは運次第のランダムかなと思ったけどリロードして試すと再現性がある

薄いところだけ無視すると一回目は 2 と 3 だけ消えて 1 は残るという変な状態になるから console.log デバッグのときは Strict モードを一時的に無効にしたほうがわかりやすいかも

ちなみに原因になったライブラリは props で受け取った値が変わったときに state を更新するために useMemo + setState するとか変なことしてるのが多くて色々辛かった

useMemo(() => {
setState(value_prop)
}, [value_prop])

みたいなの
メモの中で副作用起こさないでほしいし useEffect か ref に保持して if 文でやってほしい
React で親コンテキストに伝播させる
React のコンテキストの仕組みは useContext を使ったコンポーネントからもっと近い親のプロバイダーコンポーネントの value に渡された値を取得するというもの
ネストしたときに一番近いものを参照できていいんだけど あるイベントが起きた回数を共有するみたいなときに最も近いコンテキストだけじゃなくて更に上にも伝わってほしい
標準の仕組みでそういうことをするものはなさそうなので 自分でそうなるように作る

基本の形

const CounterContext = createContext()
const CounterProvider = CounterContext.Provider
const useCounter = () => useContext(CounterContext)

const useNewCounter = () => {
const [count, setCount] = useState(0)
return {
count,
up: () => setCount(x => x + 1)
}
}

const Parent = () => {
const counter = useNewCounter()
return (
<CounterProvider value={counter}>
<Button />
<hr />
<Child />
<hr />
<Button />
</CounterProvider>
)
}

const Child = () => {
const counter = useNewCounter()
return (
<CounterProvider value={counter}>
<Button />
<Button />
</CounterProvider>
)
}

const Button = () => {
const counter = useCounter()
return (
<button onClick={counter.up}>{counter.count}</button>
)
}

単純にネストしただけなので これだと Child の中のボタンを押しても Child のカウンターしか増えない
Child をこうして Parent のカウンターの up も呼び出す

const Child = () => {
const counter = useNewCounter()
const parent_counter = useCounter()
const chain_counter = {
...counter,
up: () => {
counter.up()
parent_counter.up()
},
}
return (
<CounterProvider value={chain_counter}>
<Button />
<Button />
</CounterProvider>
)
}

毎回書きたくないのでフックにまとめる

const useChainCounter = () => {
const parent_counter = useCounter()
const counter = useNewCounter()
return {
...counter,
up: () => {
counter.up()
parent_counter?.up()
}
}
}

const Child = () => {
const chain_counter = useChainCounter()
return (
<CounterProvider value={chain_counter}>
<Button />
<Button />
</CounterProvider>
)
}
PowerShell で◯◯したい
たまにしか使わないので PowerShell で◯◯したいってなったときに覚えれなくて毎回調べてるのでメモ

▶ コマンドを探したい

Get-Command

PS C:\Users\WDAGUtilityAccount> get-command

CommandType Name Version Source
----------- ---- ------- ------
Alias Add-AppPackage 2.0.1.0 Appx
Alias Add-AppPackageVolume 2.0.1.0 Appx
Alias Add-AppProvisionedPackage 3.0 Dism
...
...

Alias, Function, Cmdlet, Application と実行できるコマンドは種類問わず探せる

引数で検索ワードを指定できる
* や ? でワイルドカード指定もできる

PS C:\Users\WDAGUtilityAccount> get-command select*

CommandType Name Version Source
----------- ---- ------- ------
Alias select -> Select-Object
Cmdlet Select-Object 7.0.0.0 Microsoft.PowerShell.Utility
Cmdlet Select-String 7.0.0.0 Microsoft.PowerShell.Utility
Cmdlet Select-Xml 7.0.0.0 Microsoft.PowerShell.Utility

PS C:\Users\WDAGUtilityAccount> get-command i?x

CommandType Name Version Source
----------- ---- ------- ------
Alias iex -> Invoke-Expression

▶ alias を探したい

エイリアス名から実行されるコマンドを探すなら Get-Command でいいけどその反対
コマンドからそのコマンドを実行するエイリアスを探す
直接的なコマンドはなさそうなので Get-Command の結果から ResolvedCommand の Name が目的のに一致する行を探す

PS C:\Users\WDAGUtilityAccount> (get-command *)| ? { $_.ResolvedCommand.Name -eq "where-object" }

CommandType Name Version Source
----------- ---- ------- ------
Alias ? -> Where-Object
Alias where -> Where-Object

PS C:\Users\WDAGUtilityAccount> (get-command *)| ? { $_.ResolvedCommand.Name -eq "set-location" }

CommandType Name Version Source
----------- ---- ------- ------
Alias cd -> Set-Location
Alias chdir -> Set-Location
Alias sl -> Set-Location

データとして使える形式でなくて良ければ help を見れば書いてる

PS C:\Users\WDAGUtilityAccount> help cd

NAME
Set-Location

...
...

ALIASES
sl
cd
chdir

...
...

▶ コマンドの実行結果を grep したい

Select-String でテキストをフィルタできる

PS C:\Users\WDAGUtilityAccount> echo "a1`nb1`na2`nb2" > text.txt
PS C:\Users\WDAGUtilityAccount> cat text.txt
a1
b1
a2
b2
PS C:\Users\WDAGUtilityAccount> cat text.txt | select-string a

a1
a2

新しい PowerShell だとマッチしてる部分の色が変わる

PowerShell ではパイプで受け取るものはオブジェクトの配列
文字列以外のオブジェクトになってることもある
Select-String は文字列として検索するので文字列化されたものが対象になる

PS C:\Users\WDAGUtilityAccount> dir

Directory: C:\Users\WDAGUtilityAccount

Mode LastWriteTime Length Name
---- ------------- ------ ----
d-r--- 11/5/2022 7:18 AM 3D Objects
d-r--- 11/5/2022 7:18 AM Contacts
d-r--- 11/5/2022 7:18 AM Desktop
d-r--- 11/5/2022 7:18 AM Documents
d-r--- 12/10/2022 4:37 PM Downloads
d-r--- 11/5/2022 7:18 AM Favorites
d-r--- 11/5/2022 7:18 AM Links
d-r--- 11/5/2022 7:18 AM Music
d-r--- 11/5/2022 7:18 AM Pictures
d-r--- 11/5/2022 7:18 AM Saved Games
d-r--- 11/5/2022 7:19 AM Searches
d-r--- 11/5/2022 7:18 AM Videos
-a---- 12/10/2022 5:16 PM 13 text.txt

PS C:\Users\WDAGUtilityAccount> dir | select-string Do

Documents
Downloads

PS C:\Users\WDAGUtilityAccount> dir | select-string 2022

dir で得られるオブジェクトは DirectoryInfo 型か FileInfo 型
DirectoryInfo 型は文字列化すると Name プロパティの値になる

PS C:\Users\WDAGUtilityAccount> (dir)[0].name
3D Objects
PS C:\Users\WDAGUtilityAccount> (dir)[0].tostring()
3D Objects

Do で検索すると Name プロパティに Do を含む Documents はマッチするけど 2022 で検索しても日付は検索対象にならないのでマッチしない

FileInfo 型は特殊で通常の文字列化ではなく ファイルの中身を読み取って検索する

PS C:\Users\WDAGUtilityAccount> dir | select-string b

3D Objects
text.txt:2:b1
text.txt:4:b2

ファイルの中身を検索した場合は ファイル名とマッチした行数も表示される

Get-Alias では文字列化するとエイリアスの名前になる
Name として表示されてる「(エイリアス名) -> (参照先)」は DisplayName プロパティ
文字列化した内容にエイリアスの参照先は含まれないので参照先では検索できない

PS C:\Users\WDAGUtilityAccount> get-alias

CommandType Name Version Source
----------- ---- ------- ------
Alias % -> ForEach-Object
Alias ? -> Where-Object
Alias ac -> Add-Content
...
...

PS C:\Users\WDAGUtilityAccount> get-alias | select-string Whe

where

「?」 がでなくてエイリアスの名前の方に whe を含む where がマッチする

▶ ファイルをダウンロードする

Invoke-WebRequest

古い PowerShell だと wget や curl がエイリアスに設定されていたけど新しい PowerShell だと iwr のみで wget などは使えない
最近は標準で curl.exe が存在するので紛らわしいからだと思う

Invoke-WebRequest http://localhost/test.zip -OutFile test.zip

ダウンロードする場合はファイル名が必須
自動でつけてくれない
curl で -O を使えば自動でつけてくれるのでこっちのほうがいいかも

PowerShell で curl を使う場合は名前をつけて保存するときにリダイレクトを使わないように注意が必要
PowerShell のリダイレクトは文字列化する変換処理が入るのでバイナリを保存するときに使うと壊れる

古い PowerShell だと Invoke-WebRequest が IE 設定を参照するらしくてエラーになることがある
IE を参照しないよう -UseBasicParsing オプションをつけると回避できる
curl を使うでもいいけどデフォルトでは curl は Invoke-WebRequest にエイリアス設定されてるので curl ではなく curl.exe にしないと Invoke-WebRequest が実行されてしまう

▶ コマンドの実行時間を測る

Measure-Command

ブロックの中に計測したいコマンドを書く
コマンドの出力はされない

PS C:\Users\WDAGUtilityAccount> function fib ($a) { if ($a -lt 2) { $a } else { (fib ($a - 1)) + (fib ($a - 2)) } }
PS C:\Users\WDAGUtilityAccount> Measure-Command { fib 30 }

Days : 0
Hours : 0
Minutes : 3
Seconds : 19
Milliseconds : 24
Ticks : 1990247069
TotalDays : 0.00230352670023148
TotalHours : 0.0552846408055556
TotalMinutes : 3.31707844833333
TotalSeconds : 199.0247069
TotalMilliseconds : 199024.7069

ブロックの中で Write-Host にパイプするとコマンドの出力も見れる

▶ select と where

Select-Object と Where-Object へのエイリアス

SQL や LINQ と同じ感じで where は行をフィルタして SELECT は列をフィルタする

PS C:\Users\WDAGUtilityAccount> $obj1 = [PSCustomObject]@{ foo = 1; bar = 2 }
PS C:\Users\WDAGUtilityAccount> $obj2 = [PSCustomObject]@{ foo = 3; bar = 4 }
PS C:\Users\WDAGUtilityAccount> $obj1, $obj2

foo bar
--- ---
1 2
3 4

PS C:\Users\WDAGUtilityAccount> $obj1, $obj2 | where foo -eq 1

foo bar
--- ---
1 2

PS C:\Users\WDAGUtilityAccount> $obj1, $obj2 | where bar -eq 4

foo bar
--- ---
3 4

PS C:\Users\WDAGUtilityAccount> $obj1, $obj2 | select foo

foo
---
1
3

PS C:\Users\WDAGUtilityAccount> $obj1, $obj2 | select bar

bar
---
2
4

PS C:\Users\WDAGUtilityAccount> $obj1, $obj2 | select bar, foo

bar foo
--- ---
2 1
4 3

多くのオブジェクトは普通に表示しても全部のプロパティを表示してくれない
select で * を指定して全部を表示できる

PS C:\Users\WDAGUtilityAccount> dir | where name -eq Desktop

Directory: C:\Users\WDAGUtilityAccount

Mode LastWriteTime Length Name
---- ------------- ------ ----
d-r-- 11/5/2022 7:18 AM Desktop

PS C:\Users\WDAGUtilityAccount> dir | where name -eq Desktop | select *

PSPath : Microsoft.PowerShell.Core\FileSystem::C:\Users\WDAGUtilityAccount\Desktop
PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\Users\WDAGUtilityAccount
PSChildName : Desktop
PSDrive : C
PSProvider : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : True
Mode : d-r--
ModeWithoutHardLink : d-r--
BaseName : Desktop
ResolvedTarget : C:\Users\WDAGUtilityAccount\Desktop
Target :
LinkType :
Parent : C:\Users\WDAGUtilityAccount
Root : C:\
FullName : C:\Users\WDAGUtilityAccount\Desktop
Extension :
Name : Desktop
Exists : True
CreationTime : 11/5/2022 7:18:11 AM
CreationTimeUtc : 11/4/2022 10:18:11 PM
LastAccessTime : 12/10/2022 7:28:05 PM
LastAccessTimeUtc : 12/10/2022 10:28:05 AM
LastWriteTime : 11/5/2022 7:18:14 AM
LastWriteTimeUtc : 11/4/2022 10:18:14 PM
LinkTarget :
UnixFileMode : -1
Attributes : ReadOnly, Directory

ブロックを使って各行に対して任意の式を適用することもできる

PS C:\Users\WDAGUtilityAccount> 1,2,3 | select { $_ + 10 }, { $_ * $_ }

$_ + 10 $_ * $_
--------- ---------
11 1
12 4
13 9

PS C:\Users\WDAGUtilityAccount> 1..10 | select { $_ }, { fib $_ }

$_ fib $_
---- --------
1 1
2 1
3 2
4 3
5 5
6 8
7 13
8 21
9 34
10 55

▶ 要素数を数える

Measure-Object

PS C:\Users\WDAGUtilityAccount> dir Do*

Directory: C:\Users\WDAGUtilityAccount

Mode LastWriteTime Length Name
---- ------------- ------ ----
d-r-- 11/5/2022 7:18 AM Documents
d-r-- 12/10/2022 4:37 PM Downloads

PS C:\Users\WDAGUtilityAccount> dir Do* | Measure-Object

Count : 2
Average :
Sum :
Maximum :
Minimum :
StandardDeviation :
Property :

PS C:\Users\WDAGUtilityAccount> gcm * | Measure-Object

Count : 2515
Average :
Sum :
Maximum :
Minimum :
StandardDeviation :
Property :
Windows Sandbox で Windows Terminal を使う
未だに標準だと入ってないみたい
入れようにもサンドボックスの中ではストアが使えないのでストアを見れないしブラウザからインストールボタンを押してもストアが無いので何も起きない

仕方ないので Github の releases ページからインストーラーをダウンロードする
https://github.com/microsoft/terminal/releases

Windows10 と Windows11 版のそれぞれに msix ファイルと zip ファイルがある
zip の方は Windows10_PreinstallKit.zip というものでこれを先にインストールしないと msix のインストールに失敗する

ストアが無いせいか msix や appx ファイルはエクスプローラーから開けないので PowerShell でインストールする
zip の中は arm/arm64/x86/x64 とアーキテクチャごとの Microsoft.VCLibs の appx があって自動でやってくれなそうなので環境にあったのを選んでインストール
完了したら Windows Terminal 本体の msixbundle をインストール

Add-AppxPackage (zip の中の appx)
Add-AppxPackage (Windows Terminal の msixbundle)

新しい方の PowerShell は Add-AppxPackage コマンドレット自体はあるけどサポートしてないエラーが出るので古い方でやる