repository の読み方
repository はリポジトリと読んでます
ネットでレポジトリと書いてる人を見かけました
どっちでも良さそうな部分ですが なんか違和感
と思ったのも束の間
いつもどっちで書いてたっけ?と心配になるくらいレポジトリも自然に見えてきました

そもそもどっちが一般的なんでしょうか
re は基本 リ と読みたいですが re から始まる英単語を思い浮かべると 繰り返しの意味の re をプレフィックスとしている retry, remind, react, rename などは リ でそれ以外で re から始まる render, rest, rectangle, replica などは レ です
もしかしてプレフィックスの re かどうかで分かれるの?
それなら repository は レ ?

とりあえずググってみます
Wikipedia ではリポジトリでした
https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA
「リポジトリまたはレポジトリ」と記載されていて リポジトリの方が主です
ページタイトルや本文中は リ になっています

また Google 検索結果のヒット数を見てみると
"リポジトリ" → 約 9,190,000 件
"レポジトリ" → 約 607,000 件

圧倒的にリポジトリです
リポジトリ読みの方が一般的のようですね
window が load 済みか知りたい
HTML のパース時に実行されるスクリプトを埋め込めるなら

window.loaded = false
window.addEventListener("load", () => {
window.loaded = true
})

というスクリプトを実行して window.loaded 変数を見るようにできる
ただ いつ実行されるかわからない場合には上のスクリプトを実行してもすでに load イベントが起きたあとだと検出できない
いつ実行されるかわからない状況で すでに load イベントが起きたのかまだなのかを判断したい
window の load イベントじゃなくて document の DOMContentLoaded なら document.readyState でわかるけど window の load だとそれらしいプロパティがない

直接的なプロパティはなさそうだけど load などの各イベントがいつ起きたかを保持するプロパティがどこかにあった気がするので探すと performance.timing にあった
load イベントが発生する前は performance.timing.loadEventEnd は 0 だけど load イベントが起きるとそのタイムスタンプが入ってる

const windowIsLoaded = () => !!performance.timing.loadEndEvent

という感じで判定すれば良さそう
Node.js 16 の EOL が早まるみたい
https://nodejs.org/en/blog/announcements/nodejs16-eol/

もともと Node.js 16 で OpenSSL 3 に切り替える予定だったけど間に合わなくて 1.1.1 になってて このバージョンの OpenSSL の EOL は 2023/09/11 らしい
Node.js 16 の EOL はもっと先の予定だけど EOL を迎えたバージョンの OpenSSL を使い続けるのは脆弱性リスクがあるし OpenSSL 3 にすると互換性の問題もあるしということで Node.js 16 の EOL を OpenSSL 1.1.1 に合わせて早めることにしたみたい

本来の予定だと
https://nodejs.org/en/about/releases/

LTS バージョンごとの Active LTS 開始 / Maintenance LTS 開始 / EOL 日付は

v14  2020-10-27  2021-10-19  2023-04-30
v16 2021-10-26 2022-10-18 2024-04-30
v18 2022-10-25 2023-10-18 2025-04-30

いつも通りだけど毎年 10 月末に新しい LTS がリリースされる
約 1 年経つとメンテナンス LTS に切り替わる
それから約 1 年半後の 4 月末で EOL

Node.js 16 は 2024/04/30 が 2023/09/11 に変わるということみたい
14 の EOL から半年もないので結構短め

最近までは毎年の LTS が変わるたびに大きめな新機能が入ったりで毎年更新してたけど 16 からはそこまで大きく変わらないし しばらくは 16 でいいかななんて思ってたけど 今年も早めに 18 に切り替えて行く方がいいのかも
iOS16 の Safari で @container が使えるようになる
iOS16 が出ると聞いたので Safari の新機能は何が増えるのかなと調べてみるとコンテナクエリが使えるようになるようです
https://webkit.org/blog/12824/news-from-wwdc-webkit-features-in-safari-16-beta/

今でもメディアクエリでページの幅に応じてスタイルを切り替えられますが 実際に欲しいのはページ全体の幅ではなく特定の要素の幅に応じて切り替えられるものです
コンテナクエリではこれができるようになります
一応 JavaScript の ResizeObserver を使えばできなくはないですし Polyfill も作れると思いますが こういうスタイルに関するものはできるだけ CSS で書きたいので嬉しい機能です

この機能は Chrome も結構前に対応したはずだし これで気軽に使っていける……と思っていたら Chrome はまだフラグが必要な developer trial 中のようです
https://chromestatus.com/feature/6525308435955712
In developer trial (Behind a flag) とあります

少し前に Safari は :has の対応もありましたし 最近は Safari の方が CSS 関連機能の実装が速いですね
仮想スクロール
思ってた以上に簡単に作れた
行の中身のレンダリングはシンプルに HTML の文字列を返して innerHTML の設定
ムダが多いので実際に使うなら適当なライブラリで差分更新したほうが良さそう

行の座標調整は position + top にしようかと思ってたけど transform の方がスタイルの指定が少なく済みそうだったので transform
行ごとに座標指定よりは表示範囲を div にまとめて この div の表示位置を調整する方が行が多い場合に軽くなりそうだったのでまとめた

<!doctype html>
<meta charset="utf-8" />

<style>
.box {
width: 300px;
height: 200px;
border: 1px solid gray;
}

.item {
height: 50px;
}
</style>

<script type="module">
const vscroll = ({ box_elem, items, item_height, renderItem }) => {
box_elem.style.overflowY = "scroll"
const box_height = box_elem.clientHeight

const window_elem = document.createElement("div")
const contents_elem = document.createElement("div")
contents_elem.style.height = `${items.length * item_height}px`
contents_elem.append(window_elem)
box_elem.replaceChildren(contents_elem)

const render = () => {
const top = box_elem.scrollTop
const first_index = ~~(top / item_height)
const gap = top % item_height

const item_num = Math.ceil((gap + box_height) / item_height)
const render_items = items.slice(first_index, first_index + item_num)
const html = render_items.map(renderItem).join("")
window_elem.innerHTML = html

const offset = first_index * item_height
window_elem.style.transform = `translateY(${offset}px)`
}

box_elem.addEventListener("scroll", render)
render()
}

const div = document.createElement("div")
div.className = "box"
document.body.append(div)

vscroll({
box_elem: div,
items: Array.from(Array(1000), (_, index) => ({ text: `text${index + 1}` })),
renderItem: item => `<div class="item">${item.text}</div>`,
item_height: 50,
})
</script>
devtools のコンソール入力中に window の error イベントが頻発する
あるページで devtools を出してコンソールに入力しているとページ内にエラーが通知されました
devtools を開くと詳しくない人向けにここにコードを貼り付けないでくださいみたいに書いてるサービスは見たことがありますがコンソールの入力でエラーを通知されるのは初めてでした
そもそもコンソールに入力があったことを ページ側でどうやって検知してるのでしょうか?
そんな API が追加されたという話は聞いていませんがひっそりと追加されたのでしょうか?

ネットで調べても見つからないですし 検知して実際に通知を行ってるページがあるのですからそこのソースを devtools で見てどうやってるのかを調査しました
結果は単純で window の error イベントを検知してるだけでした
特にコンソールの入力を禁止する目的ではなかったようです

考えてみるとエラーが通知されるタイミングは「1+」のように構文的にエラーになるタイミングでした
試しに

window.addEventListener("error", err => {
console.log("ERR!", err)
})

というコードを設置したページを開いてコンソールに入力してみます
すると 「1+2」 と打とうとしたとき 「1+」 のタイミングでエラーがログに表示されました

入力中でも構文がチェックされて window の error として受け取れるようです
devtools のコンソールでしかも入力途中は不要だと思うのですけどね

気になるのはクライアントサイドのエラーを収集するために window の error イベントが起きたときにサーバにデータを送信するサービスがあるというところ
サービス側としてはコンソールで色々弄る人がいると大量のエラーが送信されます
ただこれはすでに拡張機能などによる DOM 編集でエラーが出るケースなどはあるので対処されてるでしょう

怖いのはコンソールに入力したものが意図せず送信されてるんじゃないかってところです
エラーが発生したコードも取れるかと ErrorEvent インスタンスの中を見てみました
見た感じでは error プロパティに Error オブジェクトが入っていて message に 「Unexpected end of input」 というのがあるくらいでした
もちろんエラー内容によっては null のプロパティを参照しようとしたなどもう少しわかるかもしれません
とりあえずコードの文字列への参照などはなく コンソールで色々するのを控える必要はなさそうです
ページからのエラー通知が邪魔なら devtools でコードを書き換えて error イベント時の処理の最初に return を追加するなどで回避できます
Electron のレンダラープロセスでファイルをダウンロード
レンダラープロセスでファイルをダウンロードしたいのにどこを見てもメインプロセスに通知してそっちで処理するものばかり
シンプルにレンダラープロセス内だけで完結したい

普通に fetch してファイル書き込みだけでできた
CORS の制限は受けないみたい

const fs = require("fs")

const download = async (path, url) => {
const ab = fetch(url).then(res => res.arrayBuffer())
fs.writeFileSync(path, Buffer.from(ab))
}

download("file1.jpg", "http://example.com/image1.jpg")

ファイル書き込みは Node.js の機能を使うので nodeIntegration が必要

アプリ内管理のローカル HTML のページを開いてるので セキュリティ制限をする必要はなくて有効にしてるけど外部のウェブページを開くなら nodeIntegration は危険なのでメインプロセスでしたほうが良さそう
ただ contextIsolation が有効になってたらウェブページ側のスクリプトから Node.js API にアクセスできないし 直接外部サイトを BrowserWindow で開く場合でも preload 内の処理でこれでもいい気はしてる
TypeScript で推論される型って信頼できないときがある
前回に続いてもう一つ TypeScript の微妙だったところ
型のおかげで実行時エラーがないとか言う割に any を使ってないのに普通に実行時エラーになった

const texts = ["aaa", "bbbb"]
const text = texts[10]

このコードの text は見ての通り undefined
静的解析すれば普通にわかりそうなものだけど TypeScript では string と推論される
配列型は長さは決まらないから仕方ないかもしれないけど最低限 「string | undefined」 って型になるべきじゃないの?
範囲を超えたら undefined になることが考慮されてない

なのでこういう使い方をするとコンパイルエラーにはならないのに実行時にエラーになる

const repeat3: (s: string) => string =
s => s.repeat(3)

repeat3(text)

結構困りそうなのがこれ

const [x, y] = str.split(",")

str が空文字など 「,」 が含まれない文字列だった場合 y は undefined だけど 推論される y は string
もしかしたら TypeScript ユーザには当たり前の気をつけるべき点として認識されてるのかもしれないけどこんな罠が普通にあるのはどうなのって思う



さすがにこの動きはバグ扱いされて報告されてたりするんじゃないのって思って調べるとオプションがあった
noUncheckedIndexedAccess

tsconfig.js でこれをオンにすると undefined かもって型になるみたい
わざわざ型付き言語選ぶくらいなんだからデフォルトで一番 strict なモードにしておいて各自で緩めるくらいでいいと思うけどデフォルトが微妙に緩い設定みたい
TypeScript って重すぎない?
以前 TypeScript を書いた時に感じたけどすごく重い
言語というよりは VSCode の解析がかな
エラー箇所があるとエディタ上に赤線が出て問題なくなると消える
コードの書き換え後にその更新に何秒も待たされる
遅いときだと 10 秒は待ってた気がする

TypeScript は普段使わないから TypeScript に影響する拡張機能系は一切入ってない状態
フォーマッターはあるけどフォーマット処理を呼び出さないときは影響しないはず
再起動などで改善はしない
開いてるフォルダの規模はそれほど大きくない
普段 JavaScript で開いてるフォルダのほうがその何倍もあるけど遅く感じたことはなし
JavaScript では型を書かないと言っても VSCode だと未使用変数を薄く表示したり 分かる部分は型を元に補完してくれたりある程度解析処理は動いてるはず

タスクマネージャで負荷を見た感じではメモリやディスクではなく CPU がボトルネックになってるよう
一応性能はそこまで低くはなくて普段使用に不満はない程度
Windows 11 に更新できる程度には新しい Intel の Core i5
シングルスレッド処理みたいで CPU 使用率は高いけど 100% までいってない
コア数が増える系の上位モデルにしてもコアあたりの速度が向上しないと改善はしない気がする

設定で解析対象から不要なものを除外することで高速化できるという記事を見かけた
build みたいなフォルダは含まれてないし除外できそうなのはなかった
node_modules は少し重めだけどここはデフォルトで除外されてるみたい

TypeScript は VSCode ならビルトインで対応してるからエディタ的には快適だと思ってたのにあんなに重いって なぜこれでそんな人気あるのかわからない
React の Element は使いまわしても良かったみたい
JSX で作る React の Element
DOM みたいに使いまわしてはいけないものって思ってました

あるコードをみたときに props として受け取るのがコンポーネントではなく Element
それを複数箇所で使ってました

例えば

const Component1 = () => {
return (
<Component2 elem={<Foo/>} />
)
}

const Component2 = ({ elem }) => {
return (
<div>
{elem}
<Component3 elem={elem} />
{elem}
</div>
)
}

みたいな感じのもの

他には

export default {
elem: <Bar />
}

みたいにモジュールが export する固定値が Element になっていて 全体で同じ Element が共有されるもの

正しく動かない気がしますが動くんでしょうか
こういうときってコンポーネントを渡して使う側で Element 化するか 関数で渡して使う側で呼び出して Element を受け取るとかしないといけないものだと思ってました

ただどう言う風に問題が出るのかまで把握できてなかったので色々試した……のですが 結局問題は起こせませんでした
state を使っても共有されたりしませんし 使用箇所全てで正常に動作しています
useMemo で Element をメモすることで重たい処理をスキップできていたので Element にいろいろな情報が残っていて共有すると問題が起きそうには思うのですけど

再レンダリングの有無を確認すると 前回のレンダリングのときとツリー構造の同じ場所で同じ Element が使われていればその Element は再レンダリングされてないようです
同じ場所で同じ Element でも一旦別の Element に切り替わると再レンダリングされてました

const elem1 = <Foo />
const elem2 = <Bar />

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

return (
<div onClick={() => setState(!state)}>
{state ? elem1 : elem2}
</div>
)
}

このコードで state による elem1 と elem2 の分岐をなくし 常に elem1 とすると state を切り替えても Foo 関数は実行されません
elem1 と elem2 で切り替えるようにすると 切り替えるごとに Foo または Bar 関数が実行されました

前回との比較で参照が同じ Element だとスキップみたいな扱いなのでしょうか
Element 自体がイミュータブルで中にレンダリングの情報を持たないなら共有しても影響なさそうです
Element である以上使うときに props は変更できませんし 再レンダリングはされなくても困らないはずです
Node.js 18 のスナップショット機能が思ってたのと違った
Node.js 18 の新機能でスナップショット機能が入ると聞いて期待してたのですが思ってたのと違いました

思ってたのは好きなタイミングでメモリ状態をファイルに書き込むことができて Node.js の起動時にそのファイルを指定すれば同じ状態から起動できるというもの
サーバみたいに常駐するものならともかく コマンドラインで繰り返し実行する系のツールでは node_modules のモジュールを毎回ロードするので遅いですしムダに感じます
最初からモジュールがロード済みの状態で起動できて ユーザが入力したコマンドに応じた処理だけをすれば無駄なくいい感じになりそうです

ですが実際にはそんな便利なものではなく Node.js をビルドするときに .js ファイルを指定できるというもの
指定してビルドされた Node.js 実行ファイルを実行すると その .js ファイルを実行した状態でファイルを実行できるようです
自分でビルドしないといけないので結構面倒で気軽に使うものではなさそうです
また 実行時にスナップショットファイルを指定できるわけではないので スナップショットの数だけ Node.js の実行ファイルが作られます
ライブラリやツールごとに Node.js 実行ファイルがあるようなものです
すでに Electron はそういう状態でアプリごとに Electron 本体が含まれているので Electron アプリを色々入れるとそれだけでストレージが結構圧迫されます

そんななので自分で使う機会は特になさそうです
いくつかのツールはこれに移ってインストール方法が変わるかもしれませんね
Flutter は 3 でも JSX みたいな記法なさそう
少し前に Flutter が Windows にも対応したと聞いて少し使ってみようかなくらいに思いました
ドキュメントを見てみるとやっぱり Dart は微妙だなーと思う他にも気になった部分がありました
UI は宣言的に書くと書かれていたので HTML や XML 風な物を使うのかと思ったもののプログラムぽいものしか書かれてません

ちゃんと読むと一応宣言的にはなってました
JSX を JSX なしで書いた版みたいなものです
さすがに読みづらすぎる気がします

JSX は React がわざわざ標準の JavaScript にない構文を導入してまで作ったくらいのものなんですから宣言的に UI を記述するのに十分に優れているといえます
.NET も WinForms から WPF になって XML ベースになったくらいです
それらに対して Flutter ではプログラムとして UI を記述する方針のようです

探すとやっぱり JSX のようなものを導入すべきという issue はできていて議論されていましたが Flutter 側としては作るつもりはないようです
https://github.com/flutter/flutter/issues/11609
https://github.com/flutter/flutter/issues/15922

一応非公式に JSX 風の記法を Dart コードに変換できる DSX というものもあるようですが 標準機能じゃないとなるとハードルが高いです
現状 JSX でも JavaScript 標準構文じゃなくて Babel での変換が必要になるせいで JSX を使わない記法にする場合もあるくらいですし

最近 Flutter の 3 が出ると発表があって メジャーアップデートだしもしかして UI の記法が変わったりする?
するなら使い始めてみるのもありかも なんて期待してたのですが 結局記法的には変わりないようです
対応すれば話題になりそうなものですが これといって聞かないですし ドキュメントの記法もこれまでどおりです
矢印の入力
Google のおすすめに出てきたページで「→」を楽に入力する方法として紹介されてたのが
「ー>」を変換するというものだった
「みぎ」の変換より楽と書いてたけどこれは楽なのか?
キーボードタイプ数は 4 から 3 に減るけどシフトキーが入るだけで 1 文字分以上に面倒なんだけど

「ZL」って入力するほうがはるかに速いと思う
Windows の Google 日本語入力以外だと使えないのかなと思ったけど Android/iOS の入力でも対応してたし環境を気にせず使えそう

スマホでフリック入力だと無理そうだけど それいうと 「ー>」 も入力辛いし 「みぎ」 の変換が一番速そう
SheetJS が npm から独自 CDN に移行するみたい
https://www.bleepingcomputer.com/news/software/npm-package-with-14m-weekly-downloads-ditches-npmjscom-for-own-cdn/

おすすめにこんなページが出てきました
タイトルを見てまた npm で何かあったのかと思ったら SheetJS が npm での公開をやめるみたい
理由は Github で 2 段階認証が強制されたことや別の問題もあるみたい
npm と SheetJS 間で訴訟があったみたいだけどここでは説明しないって書かれてるので詳しくはわからない
Github に issue ができてピン止めされてた
https://github.com/SheetJS/sheetjs/issues/2667

2 段階認証は自分も嫌いで強制されるのは嫌なのでわからなくもない

SheetJS って名前には聞き覚えあるし JavaScript で Excel 操作するライブラリは過去に何かで使ってたから今後対応必要になるのかもと思って調べたらこのサイトみたい
https://sheetjs.com/

たしかここって有料(?)の Pro プランがあるところで 機能は揃ってるけどスタイルかなにかを変更する機能を使うには Pro 版が必要になって無料版だとちょっとしたことをするのにも使えなかったやつだったような……
ググって出てきたそれっぽい QA
https://stackoverflow.com/questions/50110595/how-to-add-cell-border-to-sheetjs-xlsx-generated-file

それならこのパッケージは使ってなかった気がするし npm やめても別に困らないかな
WSL の Ubuntu 22.04 で Docker が起動しない
Ubuntu 20.04 の環境を 22.04 にアップグレードすると Docker が起動しなくなりました
この環境では docker.io を入れて service コマンドで起動していました

今年に入ってから WSL を入れた環境だと Ubuntu 20.04 ですが docker サービスが見つからないエラーになっていて docker.io をやめて消して リポジトリを追加してから docker-ce を入れていました
🔗 WSL2 で Docker サービスが見つからない

この環境でも docker-ce にしないといけないのかと docker-ce に置き換えましたが変わりません

sudo service docker start

では OK と表示されるのに docker に接続できず

sudo service docker status

を見ると起動していないと言われます

この現象ってちょっと前に debian の WSL であった記憶が……
🔗 WSL2 の debian で docker サービスが起動しない

このときと同じように iptables をレガシーモードにすることで起動できるようになりました

sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
sudo update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
jsdom の高速版ライブラリ happy-dom というのを見つけた
jsdom は内部で勝手に使われてる事が多くて 直接インストールして使うことはあまりないので 代替ライブラリを気にしてなかったのですが たまたま happy-dom というライブラリを見かけました
https://github.com/capricorn86/happy-dom

パフォーマンス比較を見るとかなり高速みたいです
2, 3 倍程度のもあれば 10 倍近いものまで

速いだけではなく WebComponents 機能など新しい機能もサポートしてるようです

軽く触れてみましたが どちらもブラウザの DOM 操作をエミュレートするものなのでどっちも同じ感じで使えました
ただ最初の window を取得するまでの手順は違いがありました

jsdom は JSDOM クラスのインスタンスを作成して そのプロパティとして window オブジェクトが入っています
JSDOM クラスのコンストラクタに HTML 文字列を渡せば DOM の初期状態にセットできます

const { JSDOM } = require("jsdom")

const jsdom = new JSDOM()

jsdom.window.document.documentElement.outerHTML
// '<html><head></head><body></body></html>'

const jsdom2 = new JSDOM(`<h1>a</h1>`)
jsdom2.window.document.querySelector("h1").innerHTML
// 'a'

happy-dom では Window クラスのインスタンスを作成します
それがそのまま window オブジェクトです
作成時に初期状態をセットする機能はなさそうなので 初期値を設定するには window を経由した DOM の API を使います

const { Window } = require("happy-dom")

const window = new Window()

window.document.documentElement.outerHTML
// '<html><head></head><body></body></html>'

window.document.body.innerHTML = `<h1>a</h1>`
window.document.querySelector("h1").innerHTML
// 'a'
なんで Next.js がそんなに人気あるの?
最近 Next.js を使ってるという話を見かけることが以前より増えてる気がします
昔使ったときはあまり使ってみようと思えるものでもなく使いやすくもなく Nuxt.js のほうが良い印象でした
なんというか悪い意味での企業がやってるツールって感じだったのですよね
コミュニティがやってる OSS に比べて製品ぽいわかりづらいホームページでドキュメント等もなんか見づらい感じでした

この何年かで Next.js が大きく改善した みたいな話も聞いたことはあります
良くなってるなら使ってみるのもありかなと久々に見てみました
ぱっと見ではあまり変わった気はしません
相変わらず作ってる Vercel と混ざっていてわかりづらいページです

使わなかった一番の理由である SSR のみサポートがどうなったかを見てみたのですが 変わったような話が見当たりません
SSR の無効化機能をリクエストする issue も放置気味のようです
未だに SSR 前提みたいですね

SSR は要らなくて 単純に CSR の SPA ページを作りたいのですが それをサポートしていません
Nuxt.js は 以前使ったときは SPA と SSR を全体オプションで簡単に切り替えできました
Next.js にもあっても良さそうな機能なのに無いようです
SSR できないコンポーネントはありえるので コンポーネント単位での SSR 無効化はあるらしいです
しかしアプリケーション全体として SSR の無効化ができないようです

完全にできないというわけではないらしく 探せばやってる例はあるのですが やらなくていい苦労をやってるような感じです
できはするが辛いので推奨しないと書かれていたり そういうことをするなら Next.js を使う必要はない CRA を使ったほうがいい といったコメントがついてたりもします
Next.js も Rails のような こういうファイルはここに置くとかフレームワーク側で事前に色々決められてる系で それに従えば楽に作れるけど外れると辛くなるというタイプのフレームワークのようです

楽に使えるならと使ってみようかと思いましたが そんな辛くなるなら無理に Next.js を使う必要もないので今回もやめておきます
それにしてもそんな Next.js がそこまで人気というのがよくわかりません
みんなそんなに SSR したいのでしょうか?
開発時だけではなく 本番用環境のサーバにまで Next.js が必要になってサーバサイドの自由度が減ると思うのですけど
フロントエンド都合はサーバ側に持ち込みたくなくて API を共有するだけで完全に独立したアプリケーションであってほしいという自分からするとサーバサイドに Next.js 必須になる SSR は敷居が高すぎるのですけどねー
V2 マニフェストの Chrome 拡張機能は今年で終わりみたい
そういえば V3 ってどうなってるんだっけと思って調べてみたら もう V2 が終わろうとしてた
https://developer.chrome.com/docs/extensions/mv3/mv2-sunset/

すでに今年の 1 月に private 以外の V2 拡張機能の受け入れを停止していて 6 月には private も受け入れ停止
ストア公開しない拡張機能だと 2022 年中は変わりなし
2023 年 1 月に Enterprise policy というのを設定してない限り V2 拡張機能は動作しなくなる
2023 年 6 月に完全に動作しなくなる

今年で終わりみたい
V3 はそのままじゃ使えない変更が大きくてあまり移行する気が起きない
過去にブログで公開してた拡張機能があったけど もう自分でも入れてないし 誰も使ってないと思うし更新はしないかな

ドキュメント見てたら結構前の話みたいだけど難読化してるとストアで受け付けてもらえないというのがあった
セキュリティ対策は必要だと思うけど V3 はそのせいで結構制限多いって聞く
完全に V2 同等のことができるのかな

心配だった webRequest API は一応あるみたいだけど強制インストールが必要になるとか
リクエストをブロックしたりリクエスト先を書き換えたりレスポンスを書き換えたりアクセスした URL のログを保存して分析したりと結構便利なんだけど 悪用するにしても便利すぎる機能だしね
オプションに設定するようなデータに補足情報を持たせたいときの方法
オプションに設定するデータなどで 本体のデータ以外に type や mode みたいな補足情報を持たせたいことがあります

option: { mode:"A", value: [1, 2, 3] }
option: { mode:"B", value: [4, 5] }

なんか長くなりますし ネストした複雑な構造になるとあまり見やすくないです
もうちょっとスッキリと見やすく書きたいです 

mode というキーにせず mode の値をキーにしてしまう方法があります

option: { A: [1, 2, 3] }

シンプルでスッキリして一見良さそうです
ただ オブジェクトにキーが 1 つだけという制限をつけることはできません
つまり

option: { A: [1, 2, 3], B: [4, 5] }

というふうに 複数のキーを持つオブジェクトも書けてしまいます
処理するところでキーが複数あるとエラーにするなどはできますが 記法的に書けてしまうのはあまり気持ちよくないところです
また キー名が事前にわからないので固定のキー名の方法に比べて キーやバリューへのアクセスがしづらくもなっています
見た目的には優れてますが データの構造的には良いとは言えなそうです

type と考えると class を使う方法も考えられます

class Base {
constructor(value) { this.value = value }
}
class A extends Base {}
class B extends Base {}

new A([1,2,3])
new B([1,2,3])

書き方的に悪くはないですが クラス定義など事前準備が必要なのと JSON 化できるプレーンなオブジェクトから外れてしまうのはイマイチなところです
new キーワードが邪魔ですし 本体はもともとの形にして記法だけシンプルになるように関数にします

const A = value => ({ name: "A", value })
const B = value => ({ name: "B", value })

A([1, 2, 3])
B([1, 2, 3])

これでもそれぞれに関数を準備しないといけません
関数を共通化するなら

const X = (name, value) => ({ name, value })

X("foo", [1, 2, 3])

記法の見やすさ的には少し落ちたように思います
Proxy を使って メソッドのように書けるようにします

const N = new Proxy({}, {
get(_, name) {
return value => ({ name, value })
}
})

N.bar([1, 2, 3])

短くなりましたが これがベストかというと判断が難しいところです
例が変わってきてたので揃えると

condition({ mode: "any", items: ["x=1", "y=2"] })
condition({ mode: "all", items: ["x=10", "y=20"] })

condition(N.any(["x=1", "y=2"]))
condition(N.all(["x=10", "y=20"]))
Python のファイル読み書きは改行コードの扱いが微妙
エディタの設定ができてない環境だったので作ったファイルが全部 CRLF 改行になってた
一括変換したいけど これといったツールがない
一括でファイルの文字列を置換して上書きしてくれる方法があればいいけどなさそう?

ファイルを開いて文字列置換して保存の繰り返しは自分でスクリプト書いてもすぐにできそう
Node.js はファイルを glob で取得が面倒だし PHP はインストールしないと入ってないしということで とりあえず Python にする
苦労するなんて全く思ってなかったけど Python では改行コードの扱いが少し特殊で苦戦した

CRLF でも file.read() で文字列を取得したら \r\n はなくて全部 \n になってる
読み取り時に全部 \n にして 書き込み時に OS の改行コードに合わせるらしい
そんな特殊な扱いしなくていいのに

文字列として読み取るのをやめてバイナリとして読み取って decode して文字列を取得すると特殊な扱いを受けずに済んだ
書き込みも encode したバイナリで書き込む

import sys
import glob

for arg in sys.argv[1:]:
for path in glob.glob(arg, recursive=True):
with open(path, "rb") as f:
text1 = f.read().decode()

text2 = text1.replace("\r\n", "\n")

if text1 == text2:
print("SKIP", path)
else:
with open(path, "wb") as f:
f.write(text2.encode())

print("CONV", path)
Ubuntu は init.d のスクリプトも使えるみたい
Ubuntu で systemd を使わずにサービスを起動する方法で /etc/init.d/ のスクリプトを呼び出してるのを見つけた
mysql の起動だと

/etc/init.d/mysql start

Ubuntu は systemd で管理されてるから /etc/init.d/ には何もないと思ってたけどインストールしてみるとスクリプトが配置されてた
こういうサービス共通で起動・停止できる方法があると WSL や Docker コンテナでサービスを動かすときにすごく楽になりそう

これまではサービスごとの方法でコマンドを実行してたから面倒だった
mysql だと最初に mariadb-install-db して起動は mysqld_safe
Apache や Nginx や PHP-FPM などの複数サービスでそれぞれ方法があるから全部覚えるのも大変

さっそく使ってみようとしたけど Ubuntu 限定だったみたい
CentOS 系だと /etc/init.d/ の中は空のままだった
PHP を Nginx で動かすとき index.php を公開フォルダにおかなくてもよかった
Apache の感覚で URL を rewrite して公開フォルダに配置した index.php を呼び出してそこから公開フォルダの外にある PHP ファイルを実行するものだと思ってた
だけど fastcgi_param の設定で実行するスクリプトファイルのパスを指定してる
URL のパスに応じた PHP ファイルを実行する場合は URL に応じたファイルのパスを設定するけど rewrite するような場合は URL 問わず常に同じ PHP ファイルになる
それなら直接設定ファイルにファイルのパスを書いておける
この指定は公開フォルダの内側にする必要はなし
/opt/app/static が公開フォルダだとして

fastcgi_param  SCRIPT_FILENAME  /opt/app/main.php;

と設定すれば公開フォルダ内に index.php みたいなエントリポイント PHP ファイルは要らなくなる

Apache の rewrite させる設定は何度見てもどうなってるのかよくわからないから こういうシンプルな方法でできるのはいいところ
ログイン時にメールで送られた番号の入力を要求されることが多いけど
Github とかにログインすると登録してるメールアドレスに自動でメールが送られて そこに書かれた 6 桁くらいの数字を入力しないとログインできない
2 段階認証なんて面倒なものはしたくないから 初回登録時に聞かれたらオフにしてるはずなのに
最近はそういうサービスが多い気がする
今はメールサービスはそういうことしてないから その場でメールサービスにログインして番号を確認できるけど メールサービスまでそういうこと始めたら番号の確認すらできなくなりそう
Google はすでにログイン時に番号入力必要だった気がするから Gmail で登録するとありえそう

メールサービスはログインしたままにすればよいかと思ったけど ウェブは Cookie 期限やなにかのタイミングで全 Cookie 削除がありえる
アプリでもしばらく使ってないと有効期限切れで再ログインさせられることがある
メールアドレスは用途ごとに使い分けてることが多いし 時々しか使わないアカウントは基本ログアウト状態
外出時にはタブレットにしたり 借物 PC を使ったり 普段遣いの PC を使わないかつすぐに触れる環境にないこともある
やっぱりパスワードのみでログインできるメールサービスを使うしか良い対処方法はなさそう
WSL の Ubuntu 22.04 が出た
早速入れようとしたけど wsl --install コマンドだと無効なディストリビューションって言われる
ストアにはあるのに
リストを表示すると出てこない

PS C:\windows\system32> wsl --list --online
インストールできる有効なディストリビューションの一覧を次に示します。
既定の分布は ' * ' で表されます。
'wsl --install -d <Distro>'を使用してインストールします。

NAME FRIENDLY NAME
* Ubuntu Ubuntu
Debian Debian GNU/Linux
kali-linux Kali Linux Rolling
openSUSE-42 openSUSE Leap 42
SLES-12 SUSE Linux Enterprise Server v12
Ubuntu-16.04 Ubuntu 16.04 LTS
Ubuntu-18.04 Ubuntu 18.04 LTS
Ubuntu-20.04 Ubuntu 20.04 LTS

ちょっと前に出た almalinux もないしストアが更新されても WSL 自体が更新されるまでリストは変わらない?

Ubuntu を入れると最新のエイリアスになってて 22.04 が入ることを期待して Ubuntu ディストリビューションを選んだけど インストールされたのは 20.04 だった

今のところはストアから入れるか 20.04 をアップデートしかなさそう
ローカルに開発環境がない状態でサーバサイド処理を動かしたい
サーバサイドありのウェブアプリケーションを動かして少し修正したいのだけど 開発関係のツールは一切入ってない環境
ブラウザだけならデフォルトの Edge だけで動かせるけどサーバサイドは無理
その PC へ Node.js や PHP 等を入れたいけど 追加インストールや exe を持ってきての実行は不可
WSL や Sandbox があればそこにインストールできるけど これらも入ってない
何かいい方法はないものかな

ブラウザ上で Linux が動かせる WebVM というのがあって Python は動いたけどブラウザ上という制限があるからネット接続系機能は使えない
パッケージインストールも自分のローカルのファイルのダウンロードもできない

ローカルでやることを諦めてリモートデスクトップや ssh を使おうにも接続先がない
開発環境として使える PC は外部からアクセスできるようにしてない
VPN でネットワークに入れても 直接使ってないときはシャットダウンしてる

AWS や GCP を使ってクラウドに頼ることはできるけど有料になる
お金払うほどじゃない
新規アカウントを作れば無料分がありそうだけど このためにわざわざアカウントを作るのは避けたい

この状況だと無理そうかな

Windows にスナップショットを取って完全にその状態に戻せる機能があればいいけど システムの復元機能は OS アップデート時のシステムフォルダだけだと思うし たぶんなさそう
Windows 自体を書き換えて元に戻すよりは最初から一時環境を操作して破棄できた方がいいから Sandbox のほうが向いてる
だけど Sandbox は追加インストールが必要だし Pro エディション以上限定
なにかのソフトを使いたいけどインストールできないしたくない環境でも Windows 本体部分には影響無しでインストールして試せるし セキュリティ的にも有用なんだから全部の Windows で標準で使える機能にしていいと思うんだけど
Apache Struts の読み方
Apache Struts2 で脆弱性が見つかったとかいうニュースを見かけた
結構昔に聞いたことがある名前
Apache だし Tomcat みたいな Java ライブラリだっけ??

軽く調べていたら「すとらっつ」って文字を発見
え?ストラトスでしょ??

カタカナでストラトスと入れてぐぐってみると全然ヒットしない
信じられないので動画検索でそれっぽい解説動画を見てみる
みんな「ストラッツ」って言ってる
まじかー

じゃあストラトスってなんなのさ
ぐぐると Stratos らしい

へぇー
でもまぁ脳内でストラトスで固定されてしまってるから 次に数年後に目にしたときにストラトスって読む自信はある
フロントエンドとバックエンドはどっちがメイン?
フロントエンドはバックエンドのおまけだ サブだ と主張してるのを見かけたので
正確にどっちがメインというのはなくて結局その人がどっちをメインとしてやってるか次第でしょうけど 個人的にはフロントがメインでバックはサブだと思います
言葉的にバックエンドは裏方ですし それがメインはないかなと

ここでいうフロントエンドは主にブラウザなどユーザが見て操作する部分でバックエンドはリモートなどにあるサーバを指してます
ブラウザ JavaScript の中でも React とかを使うと DOM の他に model や state と言われるところにデータを持ってそれを更新し DOM に反映するので DOM がフロントで model がバックとも言えますがそれらはすべてフロントエンドに含みます

ウェブ関係であれば HTML ファイルを直接ブラウザで開けばそれだけで画面を表示しユーザが操作できます
JavaScript を書けばプログラムを動かせるのでちょっとしたツール類などはこれだけで完結しています
ファイルを USB メモリ等で配布すればサーバを使わずに他の人にツールを使ってもらえます

流石に不便なのでサーバを用意しても サーバは単にファイルを配布するだけです
Apache/Nginx 等のフォルダに静的ファイルを置いておくだけ
別にサーバとの通信は HTTP でなく ファイルサーバに置くような使い方でも成立します

ローカルで完結するツールと違い 他人とデータを共有するものであればもう少しサーバを使う余地が出てきますが ここも USB メモリやファイルサーバ等によりデータを共有して各自がインポートするような使い方でも成立します

便利にしようとすると HTTP 通信でブラウザでページを開き サーバとデータをやり取りして他ユーザとデータを共有となってきます
この辺になってきてやっとバックエンドが出てくるものでしょう
そう考えるとバックエンドって無くても成り立つわけで ある方が便利にはなるよねってレベルのフロントエンドの補助みたいなもの
おまけというのならバックエンドのほうですよね

ウェブ以外のデスクトップ・モバイルのアプリだと ゲーム等を除けばローカルのみで十分で外部のサーバと接続する必要がなくバックエンドが用意されてないないアプリが多くあります
なのでフロント・バックと呼び分けてるのを個人的にはあまり見ないです
Rome も独自の JavaScript フォーマッターをもってた
Prettier やめて他に使える物を探して dprint にしたけど Rome もフォーマッターを持ってた
見てみると Prettier よりでオプションは少ないというかほぼなし
https://play.rome.tools/

◯ インデントがタブかスペースか(デフォルトはタブ)
◯ 行幅は何文字か(デフォルトは80)

くらい

Prettier はオプションをつけないといいつつ中途半端に一部だけ対応なんてしてしまったから なぜあれはやるのにこれはやらないの みたいな不満が多くあったし ここまで失くせばこれはこれでありだとは思う
Rust 製かつオプションがないことで速度は Prettier よりかなり速いようだし Prettier にするくらいなら Rome にして 設定で色々好みに合わせたいところは dprint で使い分けていけばいいかな
ON DELETE CASCADE 使わないの?
ON DELETE CASCADE はほぼ使うことがないみたいなのを見かけたので
普通によく使うけど使わないの?

親を消したらそれに紐づく子も消えるのが普通だと思う
RDB 的に可変個数なものは 配列ではなく別テーブルが多いのに 自動で消えないのは不便すぎ
記事を消すためにその記事に設定したタグを全部消さないと記事を消せないなんて使い勝手悪すぎる
フォルダを消したら中のファイルも消えるように自動で消えてほしい

事前に決まってるジャンルの中に記事を作る仕組みで ジャンルを消したら記事全部消えるのは困るからこういうときには使わない
消したジャンルの記事はジャンル未設定になるように SET NULL を使う

プログラム側で DELETE クエリを子のテーブルで実行してから親のテーブルで実行すればユーザに不便さを与えず外部キーで CASCADE にしなくても解決はできるけど それをするなら最初から CASCADE にしてデータベース側でやってもらえばいい
いろいろな理由で外部キー自体を使わない選択をしてるならそれでいいけど外部キーを付けてるのに NO ACTION や RESTRICT にしてプログラム側で削除ってムダにしか思えない
どれがどれに依存してるのかをプログラム中で把握しないといけないし 毎回消す作業が長くなるし 漏れが出て実行時に SQL エラーが出たりでデメリットしか無いと思う

別のところで見かけた話で 配列の中から id が 1 のものを取り出すとき JavaScript でいう find みたいに最初の一つだけを持ってくればいいのに filter で全部をチェックして要素数のチェックまでするらしい
id は重複しないはずなのに 重複してないことを取得のたびに確認するんだとか
そういうのは登録時にチェックすればいいわけで取得のたびに無意味なチェックをするとか理解不能
必要ないところで余計なチェックするほど後々困るだけだし そうでもしないと重複してるかもしれないというのはまともなクオリティには思えない
ネットで拾ったコード組み合わせてなんか動いてるけど本当に重複しないかわからないみたいな状態なのかなって心配にしかならない

外部キーだと RDB 側でちゃんと管理されるのでプログラム側で間違っててもエラーが出るだけだけど CASCADE で自動で消えればプログラム側で何もしなくていいのでエラーが出ること自体が起きない
ユーザが子を全部消さないと親を消せないようにしたい場合は CASCADE にしてプログラムでチェックはしたくないので RESTRICT などにしておいて SQL エラーで消せるかどうかを確認する
でもそういうケースってそこまで多くないし 基本は自動で子や孫まで消してしまう方が多いから CASCADE は普通によく使うものになってる
最近 Chrome の証明書無視期間が短くなった?
https 通信で証明書が自己署名だったりドメインが違ってたり期限切れだったりのときに出てくるブラウザのエラー画面
無視してアクセスできるから 自分管理のサーバなら基本無視
頻繁に使うページでは毎回エラー画面で無視するボタンを押すのも面倒だから 自己署名の証明書をインポートしておくけど 全部のサーバでするのも面倒だから 時々しか使わないのは毎回エラーを無視する方針

以前は一度無視したら数時間~ 1 日程度はアクセスできてたはず
とりあえず最初に無視したらしばらく使う分には困らないくらいの期間
それが最近使ったら数分レベルの短時間でまたエラー画面が出るようになってた
間隔が毎回同じじゃないような気もしたので 時間以外の要因もあるのかもしれないけど
なんにしてもまともに使えない
一度無視したらブラウザ閉じるまでは無視したままでいいのに

信頼してるサーバにしかアクセスしないし 仮に Google などが偽物になっていても困らないようなことしかしない PC だしと証明書全無視でいいので --ignore-certificate-errors オプションを付けて解決した
タブやウィンドウだけに指定はできないので Chrome 全体を終了してからこのコマンドラインオプション付きで起動すると無視できる
エラー画面が出ないだけで URL の左側のアイコン表示ではエラーになってるからデフォでもいいようなと思ったけど バックグラウンド通信ではわからないし 偽ページに置き換わってたとしてそこの JavaScript が動いたり Cookie の送信の問題もあるから 流石にデフォはだめそう

Edge が標準で入ってるし 外部は Edge で内部は Chrome でと使い分けて Chrome は証明書エラー無視にすると楽かも
接続先が localhost なら証明書エラーを無視する機能はあるらしいけど 基本ローカルのネットワーク内の別ホストだから使えない
map と副作用
配列の map メソッドを使うときはできるだけ外部に影響を与えないような処理にしてる
配列の要素のそれぞれが元のデータを基に新しいデータに変換されるのみで外部に影響は一切ないという前提で読めれば読みやすいし

だけど 非同期関数だとそうもいかなかった

await Promise.all(
items.map(item => write(item.name, item.content))
)

とか
待機するためにそれぞれの返り値の Promise を受け取らないといけないから map を使う

JavaScript だとそういうことをする場合に非同期になりやすいだけで同期関数でも結果を受け取りたいなら一緒かも
この場合の fetch が同期関数だったらありえるわけだし

const contents = await Promise.all(
urls.map(url => fetch(url).then(res => res.json())
)

const contents = urls.map(url => fetchSync(url).json())

map 内での副作用禁止にこだわりすぎて わざわざ for-of をつかってそれぞれの結果を配列に push してなんてことはしたくない
外部に影響は与えても それぞれの要素に対して処理を行って結果を返して新しい配列にするという目的だから map にしてる
だけど読むときに map だし外部に影響はないからと読み飛ばすとそこで外部に影響ある処理してたりするから困る

大体の場合は非同期関数で返り値が Promise の配列だから Promise.all 系を使うはずだからそれを目印に注意するとか?
その辺に強いこだわりを持ってる人たちはどうしてるんだろう
!function(){}() の代わりに void function(){}()
最近ではブロックスコープに let/const でいいので即時関数呼び出しを使うことは減ってるものの if 文で return したいことがあるので未だに時々使います
よくあるカッコで

(function() {
//
})()

は最後を }()) とするか })() とするかで分かれたり () なので 2 文字かつ閉じカッコが必要なのが面倒です
さらに ( から始めるのはセミコロンなしスタイルと相性が悪いということで 単項演算子版を使ってます

!function() {
//
}()

! 以外でも単項演算子なら ~ や typeof を使っても動きます
! は書きやすい上に強調してる感があって 返り値を捨てる=副作用を起こすところ ということが目立っていいかなということでこれにしてました

最近 普段はめったにみない void 演算子を見て そういえばそんな演算子もあったなぁとか思っていたら ふと即時関数呼び出しに向いてそうと閃きました

void function () {
//
}()

文字数は増えましたが ! に比べて見た目が自然な感じです
! だと Boolean キャストして反転という処理でもありましたが void は何が来ようと undefined を返すだけです
返り値を捨てるという意味合いにぴったりで処理的にもムダを感じません
通常のコードで void を書くことはまずないので全体から検索するときにも ! より優れています

唯一の問題は文字数が増えることですが 打ちやすさ的には Shift+1 って結構押しづらくもあるので void のほうが早く打ててるかもしれません

知名度的には () の方法を除けば一番見るのが ! と言っても良いくらいには ! をみることはあるので ある程度 JavaScript を読んでる人には ! は伝わりそうですが void だと二度見されそうというのはありますね
Electron のバージョン上げると使いづらくなった
古い Electron のバージョンで作ってたもので JavaScript の新機能を使って機能追加したかったのでバージョンを上げてみた
最新が 18 なので 10 以上バージョンが上がってる
多少は変更いるかと思ってたけど 大したことしてるツールじゃないから大丈夫だろうと思ってたらかなり辛い

まずページ内で require が使えない
context isolation とかいうのが自動で有効になったらしい
new BrowserWindow() 時のオプションで contextIsolation を false に指定すれば require が使えるようになった

Webview 関係も変わっていて target="_blank" があるリンクを開いたときにこれまでは new-window イベントが起きていた
そのイベントで webview.src に新規ウィンドウの URL を設定したら webview で新しいページに移動できた
今だと new-window イベントが起きない
allowpopups 属性もつければ new-window イベントは起きるけど 実際に新規ウィンドウが作られてしまう
new-window イベントで preventDefault しても効果なし

さらにブラウザ拡張機能みたいに webview 内でも context isolation が行われてる
preload 内で webview ページのビルトイン関数を上書きして Electron のページ側に関数呼び出しされたことを通知してたのにそれが動かない
上書きされたのは preload のコンテキスト内だけなのでページ側の処理で呼び出される関数は上書きできてない

面倒が多いし 古い Electron のままでいいやってなった
JavaScript の新機能が使えないのは不便だけどそんなに更新するものじゃないし
React18 が出た
https://reactjs.org/blog/2022/03/29/react-v18.html

以前から言われてた concurrent 機能が使えるようになったみたい
Suspense とか

個人的にはあまり必要性を感じてないし 変に複雑になりそうで別になくていいかなと思う機能
リリースの説明のページ見てても 今後こうしていきたいみたいのが多くて そういうのが実装されてからでいいやという感じ
自分で直接使うよりライブラリが使うべきものみたいなことを書いてて そのライブラリの実装を待たないといけないみたいだし

複雑そうな機能な分 気になるのはファイルサイズ
見てみると react-dom のプロダクション用の minify 済みでも 130KB ほどもあるらしい
https://unpkg.com/browse/react-dom@18.0.0/umd/
これにまだ react の方のパッケージもあるし もう十分重たいパッケージになってると思う

サイズ的にも機能的にもうまく動かないときにソースコードを見て自分で対処するのが辛くなってくるレベル
軽量系に移りたいけど React 使う理由が周辺ライブラリの充実度なのでマイナーどころは難しいし ライブラリがこういう新機能を前提にしていくと Preact だと動くのかも心配になってくる
Firefox のシェア
最近話題になってて割合公開してる人を何人か見かけたので自分も調べてみた
このブログはアクセス数が少なすぎて当てにならないからメインの方

ライブドアブログの機能だと Firefox が出て無くて Unknown や その他というのがあって多分この中に入ってる
詳細な数値をみるために Google Analytics を見ると

2022 年 3 月分で 5.3%

もっと少ないと思ってたけど意外といた
そろそろテスト用は Puppeteer から Playwright にしようかな
ブラウザを外部から操作するのはとりあえず Puppeteer だったけど あくまでブラウザを外部から操作するためのツール
自動で HTML 内のデータを取ってきたりファイルのダウンロードなどでも使えるもの
もちろんテストにも使えるけど Puppeteer がするのはブラウザを操作するだけなのでテスト系ライブラリは別に必要
Jest とか Jasmine とかそういうのを自分で入れるか まとめてくれてるライブラリを使うか

1, 2 年ほど前に Playwright が出てきたときは まだどうなるかわからないし とりあえず Puppeteer でいいかという感じ
Playwright は複数ブラウザに対応してるメリットはあったものの Chrome だけで確認できれば十分だし
最近ではスタンダードになりつつあるし 色々機能も追加されて便利になってるみたい
Playwright はテスト用なので 最初からテスト用の機能が入ってるし テスト用途ならそろそろ Puppeteer から移行しようかなと思ってる

Playwright ってブラウザを動かす方法は Puppeteer と同じだと思ってたけど ちゃんと調べてないからどうなんだろうと思って調べてみた
https://github.com/microsoft/playwright/issues/4862

CDP で Chrome と通信してるので Puppeteer と一緒みたい
Firefox と WebKit は Chrome のに似た独自のプロトコルらしいけど Puppeteer の開発中機能で Firefox 対応があったけどあれとはまた別なのかな
VS2019 だと .NET6 が動かない?
.NET5 が出た頃 LTS じゃないし LTS の 6 出たら使おうと思ってたけど使ってなかった
そういえば .NET6 はもう出てたっけと思って使ってみようとしたけど どうすれば使えるの?
Visual Studio のアップデートで最新にしたけど選択肢に出てこない
標準では付属してないの?
SDK を Visual Studio 外でダウンロードしてインストールすれば良い?

調べてみると VisualStudio 2019 だと .NET6 はサポートしてないみたい
Visual Studio 2022 が必要みたい
一応 2019 でも手動で設定ファイルを変更することで .NET6 を扱えるようだけど完全な機能はつかえないらしい
あと GUI での選択肢でちゃんと表示されず直接設定ファイルを扱わざるを得なくなるとか

2019 から 2022 はアップデートじゃなくて別物のインストールになるからあまりやりたくないんだけど
容量的にも動作的にも重くなるし
2015,2017,2019,2022 って種類多いし アップデート対応してほしい

実際調べてたときに 2019 でも使えるようにして欲しいって声はよく見かけた
だけど結局 2022 以降のままみたい
今だともう 2022 が正式リリースされてるけど .NET6 が出た頃はまだ Visual Studio 2022 はプレビュー版のみだったらしく フレームワークの LTS が出てるのにそれを使うツールがプレビュー版しかないっていうおかしな状態だったみたい
今でもまだ 2022 が出てそれほど経ってないから 2019 のほうが安定していて 2019 で動かしたい人は多そう

特に企業だと無料の Community エディションじゃなくて有料エディションが必要だから 2022 への移行は時間がかかりそうだし 新しいバージョンが出るたびに買ってくれないことも多そう
今でも 2010 や 2015 みたいな結構前のバージョン使い続けてるって話をたまに見かけるし

完全対応してないのをムリに使うことはしないとすれば 2019 は .NET5 まで
.NET5 は LTS じゃないしサポート終了は今年の 5 月でもう 50 日もない
.NET Core 3.1 は LTS で 2019 でも使えるけど今年の 12 月まで
今から新しく作ったり .NET Framework で作ったものを移植する先にはならなそう
https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core

それだと昔ながらの .NET Framework しか候補にならない
Visual Studio 2019 は .NET Framework 用で .NET にするなら Visual Studio 2022 以降という扱いなのかも
.NET Framework はもう更新されないと言われてたけど ARM 対応で 4.8.1 を出す(もう出てる?)という話もあるし やっぱりなんだかんだ更新される気もする
Windows 11 を出さないと言ってたのに出すのが Microsoft なんだから
実際 Visual Studio 2019 のままで .NET Framework を使い続ける企業が多くて .NET6 への移行が進まずやっぱり方針変えますとか言ったりもしそう

.NET Framework のままでも C# の新機能が使えるなら別にいいと言えばいい
Linux で動かすつもりないし
調べてみると .NET Framework でも使える機能は多いらしいけど 追加の設定やパッケージの導入が必要になって 意識しなくてもいいってレベルではなさそう
1, 2 年もすれば更に色々変わって便利になってるかもしれないし .NET6 関係はもうしばらく放置でいいかな

最近だと React Native や Flutter といったクロスプラットフォーム開発フレームワークが Windows でも使えるようだし この辺使ってみるのもありかなと思ってる
React に新しいドキュメントができてる
まだ Beta 版のようですが新しいドキュメントのサイトがありました
https://beta.reactjs.org/

今ではフックを使った関数コンポーネントが当たり前になったのに 現行のドキュメントだと主な例はクラスコンポーネントのままで フックはオプションとして別のところにまとまってるようなものでした
新しい方ではフックを使った関数コンポーネントに書き直されています
その他 解説用に図が追加されていたり 画面内で例を動かしていてその場で少しコードを変更してみて動作がどう変わるかを確認できたりと色々改善されています

ただ MDN のときも思ったのですが 最近よくあるこういう見た目のサイトってあまり好きじゃないです
見た目的には現行ドキュメントのほうが好きです
フォント的にも現行のもののほうが読みやすいです

リーダーモードを使うと少し見やすくなりますが こういうサイトだとエディタ部分などで表示が変になるんですよね
FAST ってライブラリ見つけた
Microsoft の Fluent UI ライブラリを見てると WebComponents 版もあった
https://github.com/microsoft/fluentui/blob/master/packages/web-components/README.md
https://docs.microsoft.com/en-us/fluent-ui/web-components/

説明を見ると中では FAST (@microsoft/fast-foundation) というものを使ってるらしい
https://www.fast.design/

Fluent UI のコンポーネントのソースコードを見ても @microsoft/fast-foundation からインポートしたクラスを継承していたりでコンポーネントの機能的には FAST の方がメインになっていて Fluent UI 的な見た目だけ適用してそう
FAST は URL 的にもデザインと書かれているし ドキュメント中にはデザインシステムというワードもあるし WebComponents で標準的なコンポーネントにデザインを簡単に適用できるライブラリってことでいいのかな

FAST 自体が提供しているコンポーネントのプレビューはここで見れて 結構種類が色々ある
https://explore.fast.design/
Codeception ってウクライナ発だったんだ
最近はロシアとウクライナの戦争のせいで あちこちの OSS のウェブサイトのヘッダなどにウクライナをサポートしようというメッセージが出てきてます

CodeceptJS のドキュメントを見ていた時もそんな感じのメッセージが出ていて最近よくあるやつねーくらいに思っていたのですが よくみると

CodeceptJS was born in Ukraine

え?このツールってウクライナで作られたプロダクトだったんだ
じゃあ元になった PHP の Codeception もかなと思って見に行くと

Codeception

Codeception was born in Kyiv 10 years ago. Today Kyiv is now under rocket attack. You can help to stop this madness. Ukraine needs your support!

10 年前にキエフで生まれたそうです
知名度のある OSS はだいたい欧米で一部中国くらいに思ってたので意外でした
do 式はセミコロンなしスタイルと相性悪いかも
未だに stage1 なのでもうあまり期待してない do 式ですが Rust などの言語に見られるような 最後に評価した値を返す仕組みがあります

const value = do {
if (foo) {
bar
} else {
baz
}
}

で foo が true なら bar が value に入り foo が false なら baz が value に入るというものです

セミコロンなしスタイルで JavaScript を書く場合は「(」や「[」などの文字から始める場合に「;」から書き始めます

;[1, 2].forEach(x => console.log(x))

あまり気持ちの良いものではないので「;」から始まる文はできる限り減らしたいです
for-of が使えるようになるまでは上のような配列リテラルの繰り返しで「;」始まりをよく見ましたが for-of があれば

for (const x of [1, 2]) console.log(x)

と書くので「;」始まりにはなりません

forEach 以外で filter や map をする場合は 結果をどこかに代入したり引数として渡すので

const items = [1, 2].map(x => x * 2)

のように「[」から始まらないので 「;」始まりにする必要はないです
しかし do 式と合わせて使うことを考えると 最後に代入無しで値として式を書くので

do {
if (fn) {
const x = fn(1)
const y = fn(2)
;[x, y].map(z)
}
}

みたいな感じになります
ただ見方を変えれば ほぼここでしか使わないなら return 的な目印として見るのも良いかもしれません
セミコロンの書く書かないは各自好きにしてフォーマッタが揃えればいいと思ってたけど
セミコロンの書く書かないは各自好きにしてフォーマッタが揃えればいいと思ってたけど その問題点に気づいた
セミコロンを書かないプロジェクトでフォーマッタが自動でセミコロンを削除するように設定されてるとする
セミコロン書きたい人は書くけど最後には消される

その場合にこういうコードがあって

const items = [1, 2, 3]

セミコロン書く派の人が 1 行追加して

const items = [1, 2, 3]

(foo || bar).prop = items;

とすると意図しない動作になる

セミコロン書かない人だと「(」から始める場合は

const items = [1, 2, 3]

;(foo || bar).prop = items

とするから問題ない

セミコロン書く派の人でも行頭セミコロンが必要になって

const items = [1, 2, 3]

;(foo || bar).prop = items;

と書くことになる
それならもう末尾セミコロン書かなくて良くないって思う

まぁネットの日本語文章で「。」はあまり書かれないし 無くてもいいのに毎回書いてるような人もいるし そういうタイプなんだろうから書くんだろうけど

フォーマットすれば

const items = ([1, 2, 3](foo || bar).prop = items)

になって バージョン管理してればコミット時に差分を確認しておかしいことに気づけそうだけど 変更が多いと見落としそう
で セミコロンに文句言い始めるのが目に浮かぶ

フォーマッタの設定ではセミコロンありにしておく手もあるけど 見た目上美しくないし ムダにファイルサイズを上げるだけだから最終的なフォーマット結果はなしにしておきたい
Chrome のコンソールに文字列を出力する時
最初が文字列じゃないと 以降の文字列にクオートがつく

console.log("1", "1")
console.log("1", 1)
console.log(1, "1")
console.log(1, 1)
console.log("1", 1, "1")
console.log(1, "1", 1, "1")
1 1
1 1
1 '1'
1 1
1 1 1
1 '1' 1 '1'

Node.js だとそんなことない

これでも基本は困らないけど間に文字列を挟みたい時

console.log(1, "(", 2, ")")
// 1 '(' 2 ')'

これは嫌
この場合なら `` を使えば

console.log(`${1} (${2})`)
// 1 (2)

にできるけど 値部分がオブジェクトかもしれない

console.log(`${{x: 1}} (${{x: 2}})`)
// [object Object] ([object Object])

こうなると中身が見れない
こういうときに普段使わないフォーマット文字列が役に立つ

console.log("%o (%o)", {x: 1}, {x: 2})
// {x: 1} ({x: 2})
JavaScript でもパターンマッチングしたい
他言語使ってると JavaScript でもパターンマッチングしたいなと思います
構文的にはサポートされていないので そう思うたびに関数を作ってみたりしてます
今回はいつもより高機能にしてみました

const type_key = Symbol()

const match = (value, patterns) => {
const matchType = (type, value) => {
switch (typeof type) {
case "string":
return typeof value === type
case "function":
if (value?.[type_key]) {
return value?.[type_key] === type
} else {
return value?.constructor === type
}
case "object":
if (type === null) {
return true
} else if (Array.isArray(type)) {
return type.some(t => matchType(t, value))
} else {
return Object.entries(type).every(([k, v]) => {
return matchType(v, value?.[k])
})
}
}
throw new Error("invalid type")
}
const pattern = patterns.find(([type, where, fn]) =>
matchType(type, value) && (fn ? where(value) : true)
)
if (pattern) {
return pattern[2] ? pattern[2](value) : pattern[1](value)
} else {
throw new Error("no matched pattern")
}
}

const matchEq = (value, patterns) => match(value, patterns.map(([v, fn]) => [null, x => x === v, fn]))

使用例

const gen = () => {
const f = (value) => {
return { [type_key]: f, ...value }
}
return f
}
const A = gen()
const B = gen()
const a = A({ v: 0 })
const b = B({ v: 1 })

class C {}
const c = new C()

const values = [
new Date(),
new Date("INVALID"),
/a/g,
9,
3,
"abc",
true,
{ x: 1 },
{ y: 2 },
{ foo: "a", bar: true },
{ foo: 1, bar: "x" },
{ p: { q: new Date() } },
{ p: { q: new RegExp() } },
a,
b,
c,
]

for (const value of values) {
const result = match(value, [
[Date, v => isNaN(v), () => -1],
[Date, v => v.getFullYear()],
[RegExp, v => v.flags],
[null, v => v?.x, v => `x is ${v.x}`],
["number", v => v > 5, () => ">5"],
["number", v => v > 3, () => ">3"],
["number", () => "num"],
["boolean", () => 100],
[{ foo: "string", bar: "boolean" }, ({ foo, bar }) => `${foo} / ${bar}`],
[{ p: { q: Date } }, ({ p: { q }}) => `p:q: ${+q}`],
[A, () => "A"],
[B, () => "B"],
[C, () => "C"],
[null, () => "N"],
])
console.log("%o => %o", value, result)
}
/*
Sat Mar 19 2022 14:29:40 GMT+0900 (日本標準時) => 2022
Invalid Date => -1
/a/g => 'g'
9 => '>5'
3 => 'num'
'abc' => 'N'
true => 100
{x: 1} => 'x is 1'
{y: 2} => 'N'
{foo: 'a', bar: true} => 'a / true'
{foo: 1, bar: 'x'} => 'N'
{p: {…}} => 'p:q: 1647667780906'
{p: {…}} => 'N'
{v: 0, Symbol(): ƒ} => 'A'
{v: 1, Symbol(): ƒ} => 'B'
C {} => 'C'
*/

switch 文みたいな単純に一致したときに関数実行したいこともあるので

for (const value of [1, 2, 3]) {
const result = matchEq(value, [
[1, () => "A"],
[2, () => "B"],
[3, () => "C"],
])
console.log("%o => %o", value, result)
}
/*
1 => 'A'
2 => 'B'
3 => 'C'
*/

結局作って満足して実際に使わないんですけどね
undefined を渡すのと渡さないので動きかえるのどうにかしてほしい
最近 JavaScript の変な仕様をそろそろどうにかしてほしいみたいなことを書いてましたがそのひとつ
引数を渡さなければ undefined となるのが基本的な動作なのに明示的に undefined を渡すときと省略したときで動きが違う関数があります

console.log(new Date())
// Thu Mar 17 2022 22:39:45 GMT+0900 (日本標準時)
console.log(new Date(undefined))
// Invalid Date

わざわざ undefined なんて渡さないと言っても 実際には変数に入ってるのが undefined なのでけっこうあります
例えば

const fn = date_str => {
const date = new Date(date_str)
// ...
}

fn("2022/01/01")
fn()

引数を渡さなければデフォルトで今の時刻であることを期待してるのに Invalid Date になるというのはよくあるミスです
これの対処のために

const fn = date_str => {
const date = date_str == null ? new Date() : new Date(date_str)
// ...
}

と書かないといけないというのは不満しかないです
... を使って配列を引数に展開できるので

const fn = date_str => {
const date = new Date(...(date_str == null ? [] : [date_str]))
// ...
}

と書いても動きますが 読みやすいとは言えません

const fn = (...a) => {
const date = new Date(...a)
// ...
}

とするとよりスッキリはしますが 引数を見て何を受け取るのかわかりづらいです
可変長引数である意味はないですし 他にも引数が必要な関数だと面倒が増えます

Date の場合はデフォルトが現在日時なので それをデフォルト引数とするという方法も取れます

const fn = (date_str = new Date().toString()) => {
const date = new Date(date_str)
// ...
}

引数を書くところがごちゃごちゃしてきます
Date コンストラクタの引数のところで書くほうがいいかもしれません

const fn = date_str => {
const date = new Date(date_str ?? new Date())
// ...
}

どっちにしても デフォルト値を別に作るために new Date() を余分に呼び出すことになってますし 気持ちの良い解決策とは言えないです

Date が undefined を渡した場合に引数無しと同じ動きとなってればこんな問題は起きないんですけどね
Date に関していえば早く Temporal が実装されて Date 型を使わなくて良くなってほしいです
最近 class 全然使ってない
久々に JavaScript で class 構文を使って 最近はほぼ使うことはなくなったなと思いました
もともと class 嫌いなので積極的には使ってなかったのですが Web Components の Custom Elements で HTMLElement を継承する必要があったりで一部では使ってました
ただ Custom Elements の機能ってネイティブな HTML の要素みたいなものを自作するわけで低レイヤーよりです
考慮すべき部分も多かったり コード量も増えて気楽に使うには React や Vue みたいなものに頼るか コンポーネントに分けず lit-html を使うとかです

class を使わないというと 単純な関数だと全部を毎回引数で渡す必要があって不便に思われたりしますが class でコンストラクタで設定を受け取って保持してそれをメソッドの処理で使うというのは関数でオブジェクトを返しても一緒です
比べてみると class を使わないほうが遥かにシンプルに書けて this という厄介な存在と関わる必要もありません

例えば単純な足し算と引き算の処理で 計算結果に下限上限を設定したいとします
class だとこんな感じでしょうか

class Calc {
constructor(min, max) {
this._min = min
this._max = max
}

_fix(x) {
return Math.max(this._min, Math.min(this._max, x))
}

add(a, b) {
return this._fix(a + b)
}

sub(a, b) {
return this._fix(a - b)
}
}

const c = new Calc(10, 20)
c.add(10, 30)
// 20
c.sub(30, 25)
// 10

下限上限という設定をコンストラクタで受け取って保持して add/sub メソッドで使います
これは class を使わなくてもこう書けます

const calc = (min, max) => {
const fix = x => Math.max(min, Math.min(max, x))
return {
add: (a, b) => fix(a + b),
sub: (a, b) => fix(a - b),
}
}

const c = calc(10, 20)
c.add(10, 30)
// 20
c.sub(30, 25)
// 10

すごくスッキリしています

まぁ class でもアロー関数にすれば多少はスッキリしますけど this がいるのは変わらずです

class Calc {
constructor(min, max) {
this._min = min
this._max = max
}
_fix = (x) => Math.max(this._min, Math.min(this._max, x))
add = (a, b) =>this._fix(a + b)
sub = (a, b) =>this._fix(a - b)
}

React や Vue でも class から関数よりに変わっていってますし 特に Vue はイミュータブルで関数型という考えではなくリアクティブなオブジェクトという考えのままで this を使わなくて良くなる方法にしてますし
id と cd
データを一意に識別するためのものをなんと呼ぶか
個人的には id だと思います
ネットで見る(ソース)コードでたまに使われているのが cd というもの
ディレクトリ移動しそうに思いますが code の略だそうです
code なら cd よりももう少し見る気がします

識別子というだけだとどっちでもいいとは思いますが 個人的には違いがあるものだと思ってます
id というと自動で振られる番号でだいたいが連番になってるイメージです
code というと id みたいなものだけど 自動の連番じゃなくてなにか意味があって手動でつけられた名前って感じがします
ソースコードとかコードネームとかそういうところで使われる「コード」を見ても意味がない連番みたいなのと違い ちゃんと意味があるものって感じですし

あと id はログイン ID みたいな一部を除いて数字のみのイメージです
連番ですし
code はというと数字のみもあればアルファベット混じりもあったりってイメージです

なのでデータベースの AUTO INCREMENT や SERIAL の列の名前はもちろん id です
これらの id のことを cd や code と書かれている(ソース)コードを見ると少し混乱します
どの界隈の人なのか詳しくないですけどなんで意味のない連番が code なんでしょうか

商品コードだと数字の連番かなと思いましたがアルファベット混じりもありえそうです
それにカテゴリごとに食品なら 100 から家電なら 300 から始まるみたいな意味がある番号を割り当ててそうです
後から関連系が入ったときのために 連番を少し番号を確保して 50 番の次を 60 番にするとかもありそうです
そうなると意味があって手動でつけた番号になって code です
自動連番じゃないんですよね
簡易編集できる PDF ビュワー何がいいんだろう
最近は専用のソフトの PDF ビューワーは入れずにブラウザに入れてみてます
高速で開けますし 基本はこれで十分
ですがテキストボックスを入れたりハイライトしたりといった簡易編集機能が使えません
稀にこれらが必要になるのでなにか入れようとしたのですが 最近って何がいいんでしょう?

標準的なのは Adobe のやつ
だけど Adobe 製品って重いしそんな使いやすくないしインストール時に余計なものを入れるしであまりいれたいものではないです
以前高速だからと使っていたソフトは Sumatra PDF
これは高速な分シンプルで最小限の機能しかないので編集はできなかったはず
編集に使っていたソフトは PDF-XChange Viewer か Foxit PDF Reader
XChange の方は機能は多いけど重めで機能が多い分見づらかった記憶
Foxit は表示・編集の機能的には良かったけどその他でちょっと問題あり

Foxit の日本語版は扱いがちょっと特殊です
英語のグローバル版を使えば言語設定で言語を選べますが そこに日本語はありません
日本語サイトが別にあって そこで日本語用の別のインストーラが配布されています
英語版だと普通にダウンロードできるのに 日本語版は名前やメールアドレスや利用目的をフォームに入力する必要があります
返信されるメールにダウンロードリンクがあるようです
中身見て承認とかせず自動でしょうし 適当な一時メールアドレスをとって偽名でも良さそうですが こういうムダな手順が必要です
昔はこんなのなかったのですけどね

この日本語版だと言語設定に日本語が含まれます
日本語版は分かれてるせいで更新されるのが遅いし バグなのかアップデートが正常に行われません(英語版だと正常なのかは知らないです)
アップデートの処理が途中で停止して古いバージョンのアンインストールだけ行われて新しいバージョンがインストールされないです
どれだけ待っても終わらないのでタスクマネージャで停止するしかありません
そのままだとアンインストールされて Foxit が入ってない状態になっているので 自分で Temp フォルダからインストーラを探してきて手動でインストールする必要があります
ダウンロードし直そうとするとフォーム入力が必要になるので Temp フォルダを探してました
一度や二度ではなく毎回アップデートのたびにこれでした
開くファイルや編集時に入力するのが日本語なので日本語版にしてましたが UI は英語でもいいので英語版を使ったほうがいいのかも

そういうこともあって 他にいいのがあれば Foxit 以外にしてみたいところですが ググってもこれといって新しくて良さそうなのが見当たらないです
ベスト◯選みたいなところで出てきた知らないものだと Slim PDF というのがありましたが これも Sumatra PDF みたいに編集はできない閲覧専用みたいです
あとは以前から名前は聞いたことがあった Nitro PDF Reader
こっちは単独ではなくて Nitro PDF Pro のトライアルと一緒になってるみたいで名前やメールアドレスをいれるフォームあり
PDF Reader ってこういうのばかりですね
商用製品の簡易版とか体験版みたいのじゃなくて公式サイトが Github Pages になっているような OSS 系ってないんでしょうか
MDN のデザイン前のほうが良かった
MDN のデザインが一新された
https://developer.mozilla.org/ja/

変更に関する記事
https://hacks.mozilla.org/2022/03/a-new-year-a-new-mdn/

これまでのはどこになにがあるのかやリンクがごちゃごちゃしていて Google 検索からくる分にはいいけど MDN 内でなにか探そうとすると辛かったので その辺が整理されたのは良かったと思う
けど デザインは個人的に前のほうが好きだった

新しいのは文字が小さくて見づらいし 右側サイドバーを出したせいでコンテンツ部分の幅が小さい
一番不便なのが最後の方にある機能のブラウザごとの対応状況の表
以前はどのバージョンから使えるのか数字書いていてパット見てわかりやすかった
それが今は対応してればチェックが表示されるだけ
マウスを乗せると一部はリリース日付が表示されるけど 一覧できず一つ一つ見るしか無い
それに日付だけでバージョンがわからない

変にこったデザインよりもシンプルなままのほうが見やすくてよかったけどなぁ



その後 以前のように対応状況の表に その機能が使えるようになったバージョンが併記されるようになりました
やはり✓のみだとわかりづらいって不評だったんでしょうね