以下の内容はhttps://let.blog.jp/category/Presentより取得しました。


Edge でスクロールバーの色が変わった
最近気づいたら変わってました
黒いです
以前は気にならなかったので たぶん変わったのは Edge 123 か 124 だと思います
Chrome は今のところ変わっていません

変わったのは トップレベル(ドキュメント全体)のスクロールバーの色です
自分で div に overflow: auto をつけてスクロールバーを出してもここは変わっていないようです

色はダークテーマかどうかを反映してそうです
ためしに html 要素のスタイルの color-schema を変更してみると light だと白く dark だと黒くなりました
OS 側でダークテーマを ON にしてるとそれを引き継いで黒くなるのでいつもと違って見えたということみたいです

ダークテーマを切替可能なページなら設定に応じて color-schema を切り替えてるので自然ですが ダークテーマに対応していないページだと color-schema は設定してないことも多く見た目はライトテーマなのにスクロールバーだけ OS の設定に合わせてダークテーマになっていて不自然です

後から出てきたブラウザ仕様に合わせてページを書き換えるのは嫌なところもありますが ダークテーマをサポートしないページでも

html {
color-schema: light;
}

だけはつけておいたほうがいいかもしれません
Chrome122
Chrome122 がリリースされました!
以前も書きましたが Iterator Helpers が再度使えるようになりました

Array(5).keys().map(x => `(${x})`).drop(2).toArray()
// ['(2)', '(3)', '(4)']

色々なところで [...values] を使って一旦配列化する手間が減ります

また Set の追加メソッドも使えるようになりました
結構便利です

const a = new Set([1, 2])
const b = new Set([2, 3])
const c = a.union(b)

a // Set(2) {1, 2}
b // Set(2) {2, 3}
c // Set(3) {1, 2, 3}

他にもありますが基本的な Set のメソッドです

intersection → 重複する要素の Set
symmetricDifference → どちらかにある要素の Set
a.difference(b) → a から b を除外した要素の Set
a.isSupersetOf(b) → a が b の要素すべてを含むと true
a.isSubsetOf(b) → a の要素すべてが b に存在すると true
isDisjointFrom → 重複する要素がないと true

Set を返すメソッドはどれも新規に Set オブジェクトを作るもので破壊的なものではないです
AVIF を使おう
画像フォーマットの AVIF 形式ですが Edge 121 でやっと使えるようになったようです
https://caniuse.com/avif

Chrome では数年前から使えて Safari も 1 年以上前から使えたのですが Edge だけが使えなかったです
Chrome で表示できるのだから Edge でも表示できていいのに無効化されていました
それがとうとう使えるようになりました

前から JPEG はそろそろ捨てたいと思って WebP を使ったりもしましたが どうせなら更に新しい AVIF にしたいなと思って期待していたので待ちに待った機能です

ただし今はまだ表示できるだけ
Canvas からの出力で image/avif を指定しても対応してないです
WebP みたいにブラウザ上で作れるともっと身近になるのですけどね

あとライブドアブログは対応してないので 手元で AVIF 画像で管理していてもページに貼り付けるときは JPEG/PNG に変換するしかないです
WebP すら対応してないのであまり期待はできないです
この辺はそろそろどうにかしてほしいところです

ところで AVIF より新しい形式に JPEG XL というのもあるそうです
JPEG という名前の時点で悪いイメージしか無いですし使おうと思いません (一応性能的には AVIF よりも上という情報も見かけます)
AVIF のときは WebP より AVIF がいいなと思いましたが JPEG XL は別にいいかなってところです
それに Chrome は以前 JPEG XL を実験的にサポートしていたものの現在ではサポートをやめたそうです
AVIF で良さそうですね
DevTools でプライベートプロパティが直接操作できる
いつのころからかコンソールでプライベートプロパティを直接操作できるようになってました

const x = new class { #p = 1 }
x.#p++
// 1
x.#p++
// 2
x.#p++
// 3

外部からのプライベートプロパティ操作なのでエラーになるはずなのにエラーが起きず読み書きできてます
最初はバグ?と驚きました
昔はコンソールでもちゃんとエラーになってたはずです

便利機能だとするといつ追加されたのか気になったので調べてみたら Chrome 111 らしいです
(Chrome 111 のリリースは去年の 3 月です)
https://developer.chrome.com/blog/new-in-devtools-111?hl=ja

「その他のハイライト」というところに

デバッグを容易にするため、DevTools でプライベート クラスメンバーを使用した式の評価がサポートされるようになりました。(1381806

という形で記載されてました
重要だと思うのですがひっそりとしてます

個人的にはプライベートプロパティはほぼ使わないので全然知らなかったです

この便利機能が使えるのはコンソール内のみで eval や script タグ経由になると無効です

eval("x.#p++")
// Uncaught SyntaxError: Private field '#p' must be declared in an enclosing class
window.x = x
const script = document.createElement("script")
script.innerHTML = `window.x.#p++`
document.head.append(script)
// Uncaught SyntaxError: Private field '#p' must be declared in an enclosing class

デバッグし辛いというプライベートプロパティの最大の問題が解決されたのでこれまでより使いやすくなったと思います
ですが 使う側で回避策や機能追加のために直接プライベートプロパティを扱いたいってことはあるのでライブラリレイヤーであまり使ってほしくないというのは変わらないです
Array.fromAsync
来週に stable リリースの Chrome 121 で使えるようになる機能です
https://chromestatus.com/feature/5069575759069184

Array.from みたいなものですが非同期用です
返ってくるのは配列ではなく配列をラップした Promise です

Array.fromAsync([1, 2])
// Promise {<fulfilled>: Array(2)}

await Array.fromAsync([1, 2])
// [1, 2]

同期処理も非同期処理も対応してますが結果は Promise になります

await Array.fromAsync(function*() {
let i = 0
while (i < 3) yield i++
}())
// [0, 1, 2]

await Array.fromAsync(async function*() {
let i = 0
while (i < 3) {
await new Promise(resolve => setTimeout(resolve, 500))
yield i++
}
}())
// [0, 1, 2]

Array.from と for-of みたいな関係で Array.fromAsync は for-await-of で配列に入れるのと同じです

const it = async function*() {
let i = 0
while (i < 3) {
await new Promise(resolve => setTimeout(resolve, 500))
yield i++
}
}()
for await (const item of it) {
console.log(item)
}
// 0
// 1
// 2

Promise の配列に使うと Promise.all と同じ感じです

const promises = [1, 2, 3].map(x => Promise.resolve(x))
await Array.fromAsync(promises)
// (3) [1, 2, 3]
await Promise.all(promises)
// (3) [1, 2, 3]

ただし順番に await されるので thenable オブジェクトで then の中の処理がある場合 実行タイミングがずれます
それぞれに sleep のような処理がある場合は順番にスリープしていくので遅くなります
Promise.all の場合は全部の then を一気に呼び出すので一番長いものの待ち時間で済みます

const thenables = [1, 2, 3].map(x => {
return {
then: (onFulfilled, onRejected) => {
console.log(Date.now() % 10000)
setTimeout(onFulfilled, x * 1000, x)
}
}
})

Array.fromAsync(thenables)
.then((values) => console.log(Date.now() % 10000, values))
// 3842
// 4852
// 6867
// 9868 [1, 2, 3]

Promise.all(thenables)
.then((values) => console.log(Date.now() % 10000, values))
// 4857
// 4858
// 4858
// 7867 [1, 2, 3]

これまでのジェネレーターと同じですが 無限に続くところで使うと終わらないので注意が必要です
同期処理だと画面が固まったり早い段階でエラーになったりして気づきやすかったですが非同期になると少し分かりづらくなります
画面が固まらないですし Promise が解決される間隔がある程度あれば CPU 負荷もそこまでじゃないです

for-await-of ならイベントリスナのかわりみたいな使い方もありかなと思うのですが Array.fromAsync だとそういう使い方もできませんし どこで使えるのかはあまりイメージできてないです
Iterator Helpers が Chrome 122 で復活予定
期待してた機能の Iterator Helpers ですが 使えるようになってすぐにウェブ互換問題で使えなくなりました
🔗 Iterator helpers が使えなくなった

Chrome 122 (現 dev) から再度使えるようになるようです
https://chromestatus.com/feature/5102502917177344

ウェブ互換問題に対応するため 少し特別な扱いをしてるようです
toStringTag に違いがあるみたいです

https://github.com/tc39/proposal-iterator-helpers/pull/287
https://github.com/tc39/test262/pull/3970/files

実際に getOwnPropertyDescriptor で他のクラスと比較してみると

Object.getOwnPropertyDescriptor(Map.prototype, Symbol.toStringTag)
// {value: 'Map', writable: false, enumerable: false, configurable: true}

Object.getOwnPropertyDescriptor(Iterator.prototype, Symbol.toStringTag)
// {enumerable: false, configurable: true, get: ƒ, set: ƒ}

ほかは Map みたいに writable が false で value に値が入ってます
Iterator では getter/setter になっています

通常利用では気にする必要ないかと思いますが 特殊なもののようです



気になったので prototype 構造がどうなってるか少し調べてみました

Iterator.prototype.__proto__ === Object.prototype
// true

const i1 = [].values()
const i2 = i1.take(1)
const i3 = (function*(){})()
const i4 = i3.take(1)
const i5 = i3.map(x => x)

// Iterator Helpers で得られるオブジェクトの prototype は
// 元やメソッドが違っても同じ

i2.__proto__ === i4.__proto__
// true

i2.__proto__ === i5.__proto__
// true

// Iterator Helpers とそれ以外は別

i1.__proto__ === i2.__proto__
// false

// いずれも Iterator を継承してる

i1.__proto__.__proto__ === Iterator.prototype
// true

i2.__proto__.__proto__ === Iterator.prototype
// true

i3.__proto__.__proto__.__proto__ === Iterator.prototype
// true

Iterator を継承したオブジェクトの toStringTag は他と同じで writable が false で value に値が入ってる

Object.getOwnPropertyDescriptor(i1.__proto__, Symbol.toStringTag)
// {value: 'Array Iterator', writable: false, enumerable: false, configurable: true}

Object.getOwnPropertyDescriptor(i2.__proto__, Symbol.toStringTag)
// {value: 'Iterator Helper', writable: false, enumerable: false, configurable: true}

Object.getOwnPropertyDescriptor(i3.__proto__.__proto__, Symbol.toStringTag)
// {value: 'Generator', writable: false, enumerable: false, configurable: true}
pyscript が script タグになってた
去年にブラウザで Python を動かせるとして出てきた pyscript
あれからも大きく更新されてたようです
https://jeff.glass/post/whats-new-pyscript-2024-1-1/

最初の頃は Python のコードを書く場所に py-script タグを使ってました
それが stable 版になった今だと py-script タグは推奨されず script タグを使って type に py や mpy を指定するのが推奨のようです
https://pyscript.github.io/docs/2024.1.1/user-guide/first-steps/
カスタムエレメントだと HTML として解析されてしまうことがあるからだとか

最初見たときにも script タグを使わないとエスケープとかおかしなことになりそうと思いましたが やっぱりそこが問題だったみたいです
ただすべての py-* タグが推奨されないわけではなく設定を書く <py-config> は使われてるようです

ところで script タグを使うときの type の文字列
HTML の仕様的には 今後追加されるものは module や importmap みたいに MIME type 以外の文字列で追加予定なのでデータブロックを表すために type に指定するのは有効な MIME type で JavaScript 用に予約されてるもの以外とされてたはずです
https://html.spec.whatwg.org/multipage/scripting.html#the-script-element
py や mpy を使ってるので pyscript ではそれには則ってないみたいですね
Node.js 21.4 で Dirent に parentPath が追加された
Welcome to Node.js v21.4.0.
Type ".help" for more information.
> fs.writeFileSync("foo/bar/file", "text")
undefined
> await fs.promises.readdir("foo/bar")
[ 'file' ]
> await fs.promises.readdir("foo/bar", { withFileTypes: true })
[
Dirent {
name: 'file',
parentPath: 'foo/bar',
path: 'foo/bar',
[Symbol(type)]: 1
}
]

path と一緒ですね
どういう場合に違うんだろうといろいろ試しても違いがわからないのでドキュメントを読むとエイリアスだそうです

どうして全く同じものを?と思いましたが元々は path という名前でした
なのに実体は親のパスです
Dirent に対して path と言われたら name も含めたフルパスを期待するのに readdir の引数に渡されたパスになっています
これが紛らわしいので path を置き換える目的で追加されたようです
dnf5 速い
昔はそんなに気にならなかったのですが 最近って dnf がすごく重たく感じます
特に各コマンドの前に行われるリポジトリの更新に時間がかかってます
ダウンロードは仕方ないとして それの前後で固まってる時間があります
パッケージ数が少ない AlmaLinux だとそれほどかかりませんが fedora ではかなり待たされるときがあります

どうにかしたいと思っていたら dnf5 が使えるようです
fedora だと dnf で dnf5 というパッケージをインストールすれば使えます
今の最新の fedora は 39 ですが 38 でもインストールできました
まだ dnf5 は正式ではないようで別パッケージとしてインストールが必要という状況だからか AlmaLinux の方には dnf5 パッケージとしては提供されてないようです

少し使ってみた感じ 同じように使えました
そして高速です
体感で結構変わります
ただ出力の見た目が変わっていて 個人的には今の dnf のほうが見やすくて好きです

実際の処理時間を比較してみました

fedora38 の Docker コンテナ環境に git をインストールします

dnf install git -y
dnf5 install git -y

事前にキャッシュはそれぞれクリアしておきます

dnf clean all
dnf5 clean all

キャッシュの管理は別になってるようです

結果は

dnf
real    1m1.498s
user 0m38.245s
sys 0m2.827s

dnf5
real    0m32.621s
user 0m13.929s
sys 0m2.522s

実時間 (real) で約 2 倍の差です
これは早く dnf5 がデフォルトになって欲しいですね

一応詳細なコマンドの実行ログはここにおいてます
https://gist.github.com/61edfbf85d436a9ac941770499e96e34
VSCode でエディタを別ウィンドウで開けるようになった
以前から欲しかった機能がやっと使えるようになりました
フローティングウィンドウとか呼ばれる機能です

ブラウザみたいにタブをウィンドウの外に持っていったら独立したウィンドウで開きたいのですがそれができなかったのですよね
ワークスペースの複製みたいな特殊なことをすればできなくはなかったですが やっぱり特殊な操作が必要で手軽なものではなかったです

それが今回のアップデートからはブラウザと同じ感じでタブを移動できます
しかも 2 つのウィンドウで同じファイルを開くと 画面分割で同じファイルを開いてるときと同じようにリアルタイムに両方に反映されます
とてもいいですね

ただ実装されるまでが長すぎて画面分割で慣れてきてしまって 最近はあんまりウィンドウ分けたいなと思わなくなりつつあったりもしますけど
Developer Roadmaps
Developer Roadmaps というサイトを知りました
https://roadmap.sh/roadmaps

使われてる画像は以前どこかで見たことある気がしますが サイトとしては知らなかったと思います

フロントエンド開発者やバックエンド開発者などが使う技術がまとまってます
https://roadmap.sh/frontend
https://roadmap.sh/backend
特定機能のためのマイナーライブラリまでは無いですが 主なものは網羅的に記載されてるようです

フロントエンドみたいなまとまりだけではなく TypeScript や Python みたいな言語単位もあれば Node.js や Flutter みたいのもありますし React や Vue みたいなフレームワーク単位もあります
また AWS や Docker や SQL などもありかなり充実してます

中を見てみるとフロントエンドだと知ってるのも多いですが バックエンドだと知らないのが多いです
これらをすべて使いこなしている人たちはすごいですね
2023/11 の TC39 ミーティングでの変更点
そういえば 11 月末なので TC39 のミーティングがやってますね
まだ続いてるみたいですが いま時点で Stage が変更されたものだと

◯ Array Grouping が Stage4
◯ Promise.withResolvers が Stage4

になったようです

すでにブラウザで使えてたこともあって地味なところ

気になっていた Iterator Helpers のその後や Temporal がいつくらいに使えそうなのかや Import Attributes のその後はどうなんでしょうね
このへんは Stage だけみてもわからないので しばらくしてから各 issue の更新をみるしかなさそうです
RHEL 系に NodeSource から Node.js をインストールする方法が変わってた
以前は shell script のファイルをダウンロードして bash にパイプする方法でした
実行すると環境に応じた repo ファイルがダウンロードされて /etc/yum.repos.d/ に配置される形でした

それがリポジトリを追加するためのパッケージをインストールする方法になっていました
rpm をインストールするとリポジトリが追加されるという形です
epel などと同じです
こっちの方が扱いやすくて良いですね

例えば Node.js 20 の場合は

dnf install https://rpm.nodesource.com/pub_20.x/nodistro/repo/nodesource-release-nodistro-1.noarch.rpm

という感じです
URL の 20 のところを 18 や 21 にすることで別バージョンに切り替えできます

これでリポジトリが追加されたので nodejs パッケージをインストールしようとすると 指定バージョンの Node.js がインストールできます
Node.js 21.2 で ESM に CJS の __dirname と __filename 相当の機能が追加された
https://nodejs.org/api/esm.html#importmetadirname
https://github.com/nodejs/node/pull/48740

import.meta.filename
import.meta.dirname

で取得できます

root@cca30b68b828:/# cat /tmp/a.js
console.log(import.meta.filename)
console.log(import.meta.dirname)

root@cca30b68b828:/# node --experimental-detect-module /tmp/a.js
/tmp/a.js
/tmp

これまでは import.meta.url から自身のファイルのパスを file:// 形式で取得してローカルパス形式に変換する必要がありました

import path from "node:path"
import url from "node:url"

const __filename = url.fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

これを毎回書くのが面倒だったのでかなり便利になりますね



ところで 18 や 20 でも使いたいなと思って Polyfill できないか考えてみました
こんなものを作ってみたのですが 期待通りには動かなかったです

import url from "node:url"
import path from "node:path"

if (!import.meta.filename) {
Object.defineProperties(import.meta, {
filename: {
get() {
return url.fileURLToPath(this.url)
},
},
dirname: {
get() {
return path.dirname(this.filename)
},
},
})
}

import.meta ってグローバルオブジェクト風に見えて 特殊なもので モジュールごとに別の実体があるのでどこかでプロパティを追加しても他のモジュールには影響しないです
プロトタイプのないオブジェクトなので プロトタイプの方を拡張することもできません

loader を使って全モジュールの最初に import.meta.filename などを追加する方法は取れなくもないですが ソースコードを暗黙的に書き換えるようなことはあまりしたくないですし 諦めてバックポートを待とうと思います(需要的に安定すればきっとされるはず)
C# 12 のコレクション式記法が良さそう
.NET 8 が出ましたね
合わせて C# 12 が使えるようになったようです
新記法が追加されたようで 少しスクリプト言語に近づきました

int[] nums = [1, 2];

という感じで [] のリテラルで配列を作れます
JavaScript や PHP や Python などの言語に近い感じで書けます

配列以外のリストやセットなどでもいいです

List<string> strs = ["foo", "bar"];
HashSet<int> hs = [1, 2];

ネストもできます

int[][] n = [[1, 2], [2, 3]];
List<List<List<int>>> ll = [[[1], [2]], [[3], [4]]];

展開もできます

int[] ii = [.. i, .. i];

この記法は JavaScript や PHP と似てるようで違って紛らわしいです
... じゃなくて .. ですし フォーマットすると変数名の間にスペースができます
Range のリテラルに使う .. を使いまわしてるみたいです

var r = 1..10;

展開自体はこれまでの new [] { 1, 2 } の記法のときからできたのかと思いましたが できないようでした
今でもエラーになります

var error = new[] { ..i };

色々できて便利ですが Dictionary は対応していないようです

Dictionary<string, bool> flags = [["foo", false], ["bar", true]];

これはエラーでした

KeyValuePair を入れる方法も試しましたがダメでした

var kvp = new KeyValuePair<string, bool>("foo", false);
Dictionary<string, bool> flags = [kvp];

'value' の必要なパラメーター 'Dictionary<string, bool>.Add(string, bool)' に対応する特定の引数がありません
って言われます
Dictionary の場合は Add で key と value の 2 つの引数を取る形になりますが この記法で書くと KeyValuePair の 1 つを渡すようになってしまって引数が足りてないとみなされているようです

なので Dictionary を使う場合は まだこれまでの記法で書く必要があります

var flags = new Dictionary<string,bool> { ["foo"] = false, ["bar"] = true };

一見便利な新機能ですが 個人的に不満もあって var が使えません
左辺に型を明示的に書く必要があります
右辺だけだと List なのか配列なのかわからないので仕方ないのですが C 系の 「型を文の最初に書く」 記法は読みづらいので好きになれません
変数宣言だとわかるように 文は var など固定の文字列で始めたいです
int 等のシンプルな型ならまだいいですが 長めの型になるとこれが変数宣言だとわかるまで少し時間がかかって読みづらいです

一応 右辺でキャストすれば左辺は var でも通るのですが 無理矢理感もあってこれでいいのか不安な感じもします
これまでの記法とあまり変わらないですし

var nums = (int[])[1, 2];
var ll = (List<List<List<int>>>)[[[1], [2]], [[3], [4]]];
ストア版 WSL が 2.0 になった
https://github.com/microsoft/WSL/releases/tag/2.0.9

9 月にプレリリースという形でリリースされていた 2.0 が正式リリースされました
2.0.9 です

メモリやストレージを自動で解放してくれたり 外部端末から IP 指定でアクセスしても WSL にアクセスできない問題を解決したり色々新機能がありますね
新機能がまとめられてる記事↓
https://www.publickey1.jp/blog/23/windows_subsystem_for_linuxwsllanwsl.html

特にネットワーク周りの問題は面倒が多かったですからね
ただ Docker 周りでまだ問題があるとかいう話も聞きます
仮想マシンという形で動作してる以上 仕方ない部分もありそうですね

ただ せっかく仮想マシンで分かれてるんだし ブリッジ接続にしてくれたほうが便利だと思ったりもします
複数の WSL ディストリビューションや WSL 内の Docker/Podman コンテナで同時にウェブサーバーを起動してることってけっこうあります
全部をホストの Windows で動作してるようにみせかけるとポートの競合が起きて面倒です
全部 3000 番に揃えることができず それぞれのポートを別にしないといけないです
その点ブリッジだとアクセスする IP アドレスが違うのでポートの競合は気にしなくていいです

WSL 内の Docker を Windows と同じ階層にあるように見せて↓みたいなことができるかは知らないですけど

Windows        192.168.10.11
WSL(1) 192.168.10.21
Docker(1) 192.168.10.22
Docker(2) 192.168.10.23
WSL(2) 192.168.10.31
Docker(1) 192.168.10.32
Docker(2) 192.168.10.33
Prettier の条件演算子のフォーマットが改善されたみたい
https://sosukesuzuki.dev/posts/prettier-curious-ternaries/

Prettier を使わなくなって結構経つので 最近どんな変化があったかは把握してなかったですが あの微妙な条件演算子のフォーマットが改善されたようです
まだ フラグ付きの実験的導入みたいですが 以前の全フラットよりは良さそうに見えますね

少し試してみようかな
JavaScript の Decorators はどんな感じになったんだろう
Lit のドキュメントを見てるとよく使われてるデコレーター
そういえば少し前の TypeScript 5.0 から Stage3 のデコレーターがフラグ無しで使えるようになったと聞きます
あまり使うつもりも無かったのと ステージが上がるのが長期的なものだったので詳しくは見てなかったですが Stage3 に上がって 1 年以上経ってるようですし TypeScript で標準でつかえるようになるということは現状の機能で JavaScript に来そうですし どんなものか見てみました

クラス専用機能みたいです
思ってたのと違う
なんか前もそんなことを言ったような気がします

クラス定義やメソッド定義の前にだけかけるようです
また 単純に関数で変換するだけでなく コンテキストオブジェクトも受け取って 初期化処理を追加するなど思ってたより複雑なものでした

const decorator = (method, context) => {
console.log(context)
context.addInitializer(function() {
console.log(this)
})
}

addInitializer はクラスのデコレーターだと クラス定義後すぐで メソッドだと各コンストラクタ内の処理として呼び出されるようです
また this を受け取るのでアロー関数ではない通常の関数を渡す必要があります

また デコレーターは値を返すことは必須ではなく 返さないとデコレーターの引数に渡されるものそのままがセットされるようです

期待してたのは もっと単純で任意の式の前におくことができてアロー関数にも適用できるものだったのですけどね
こんなこともできて 関数でラップする代わりの記法みたいなもの

const log = fn => (...a) => {
console.log("called", fn, a)
const result = fn(...a)
console.log("result", fn, result)
return result
}

const fn = @log (a, b) => a + b
fn(1, 2)
// called (a, b) => a + b [1, 2]
// result (a, b) => a + b 3

const plus1 = x => x + 1

console.log(@plus1 10)
// 11

関数でラップすればいいだけ といえばそうなのですが これができたほうが見やすく書けると思うのですよね
例えば setTimeout みたいに関数を渡すところ

setTimeout(
() => {
console.log(1)
},
1000
)

これを関数でラップすると

setTimeout(
deco(option1, option2, () => {
console.log(1)
}),
1000
)

デコレーターで書けると

setTimeout(
@deco(option1, option2)
() => {
console.log(1)
},
1000
)

明らかにこっちのほうがいいと思います
手前に書く記法でもいい感じです

foo(
value1,
value2,
@once (value) => {
console.log(value)
},
)

ただ 構文を考えると アロー関数の引数の () がデコレーターの once の関数呼び出しみたいになり 判断できなそうです
対処するなら関数を () で包まないとダメそうで そうなるなら求めてるものじゃないんですよね

この記法ではないものの 拡張機能として将来的もっと色々な使い方が考えられているようです
便利というより複雑という印象が強いですけど
https://github.com/tc39/proposal-decorators/blob/master/EXTENSIONS.md
CSS の zoom プロパティは存続するみたい
以前なくなりそうと書いたけど結局どうなったんだろうと調べてみました

https://groups.google.com/a/chromium.org/g/blink-dev/c/V7q43bgutbo/m/-7jneTl8CQAJ

の最後のコメントを見る感じ CSSWG で正式仕様にするよう決まったみたいで 削除はされないそうです
リンク先にすごく長い議論の本文がありますが 長すぎてそこまで読んでないです

標準の仕様化するなら今の一貫性のない挙動じゃなくて もっと見直してほしいと思いますが 標準化する理由が互換性の問題なので今の動作に合わせた形にしかならない気がします
また歴史的経緯で変な仕様が増えるのか
HHVM は正式リリースを作らなくなったみたい
去年末からリリース報告の更新がなくなって その後はセキュリティパッチの情報が数回あったくらいでした
どうなったんだろうと思ってましたが 先週新しい記事が投稿されてました

https://hhvm.com/blog/2023/10/27/oss-update.html

色々変更するそうです
GCC をやめて Clang にするみたいです
Clang のほうが新しいし 自然なものかなと思ってたら理由は Meta の投資が減って余裕がないからだとか
そんな良くない状況なんだ
たしかに 以前は PHP 代替として話題性もあったのに最近は全然聞かなくなりましたからね
PHP 互換を捨てると発表した頃から個人的には少し期待してましたが ついてこないユーザーばかりだったようですね

公式リリースが去年から公開されてませんでしたが 今後も作成しないようです
メンテする余裕もないみたい

一応 OSS として公開を続けるみたいですが ほぼ Meta の内部使用のためのものを公開してるだけみたいですね

この方向だとこれ以上 流行るように思えないですし 公開されてるドキュメント等も放置されたままになりそうですし もう使うことはないかも
GitLab のウェブ IDE が VSCode になってた
久々にウェブ IDE を使ったら VSCode の画面になってた
リポジトリのフォルダを VSCode で開いてるのとほぼ変わらなくて使いやすい
ファイルの保存というのはなくて まとめてコミットすることになる
ローカルで Git を使わずウェブの UI だけで使うにはすごく便利

これまで Gist においてたようなのは GitLab にしようかな
すでに公開する系を色々まとめておくためのリポジトリは用意してるし

便利すぎて Ctrl-S を押す感覚でコミットして 動作確認は Pages 画面でやればいいかみたいになってる
でも Git でバージョン管理するものとしてはふさわしくない気がする
期待通り動くようになった時点でそれまでのコミットを squash でまとめられたらいいけど
GitLab の UI を通してはできないような気がする
ブランチも master だし
ブランチのプロテクト解除してローカルで書き換えてから force push かな
でもとりあえず色々まとめておいてるだけのリポジトリでそこまで頑張る必要もないかも
Chrome 120 で CSS のネスト時にセレクタをタグ名から始められるようになる
https://chromestatus.com/feature/5070369895743488

h1 {
a {
color: dodgerblue;
text-decoration: none;
}
}

これができるようになります

これまでは 内側のセレクタは記号から始める必要がありました
ID やクラスなどのセレクタは記号から始まるので問題ないですが タグ名を書くときは簡単に書けませんでした

上記の CSS だと a のセレクタは無効です
:is() を使ったり & を使う必要があります

h1 {
:is(a) {
color: dodgerblue;
text-decoration: none;
}
}
/* or */
h1 {
& a {
color: dodgerblue;
text-decoration: none;
}
}

事前に CSS を変換するツールだと 基本的にこの一手間が不要だったので少し不便です
事前に変換するなら速度がそこまで重要視されないので 先の方まで読んでしまって セレクタなのかプロパティ名なのか判断できます
しかし ブラウザだと先まで読むことを基本許可しないので タグ名から始められないのは仕方ないものでした
この辺は実装されるまで仕様を決める段階でも色々議論されてたようです

なので無理なものと思っていたのですが Chrome 120 からはそれが緩和されてタグ名から始められるようになったようです
パフォーマンス面ではやはり劣るみたいですが できる限り高速になるようしているそうです
基本は declaration (color: red; みたいの) としてパースし始めて プロパティでないと判断でき次第 rule (h1 {} みたいの) として再パースするようです
詳細な説明は仕様の Implementation note のところに書かれてました
https://drafts.csswg.org/css-syntax/#consume-block-contents

ShadowDOM を使ってるとスコープが小さくなるので タグ名だけのセレクタを使うことが多くなります
毎回 :is() を書くのに疲れてきていたので これはとても嬉しいですね
Temporal がもうすぐ使えるようになりそう
JavaScript の不満点のひとつでもある Date オブジェクト
色々と使いづらく dayjs 等のライブラリが必須に近い状況です
一々ライブラリを入れたくないところでは自分で簡単な関数を作るのですがそれも面倒です

そんな Date に変わるものとして期待の日付系の新機能 Temporal というのがあります
https://github.com/tc39/proposal-temporal

この機能は 2021 年 3 月には Stage3 になっていたのに その後全然 Stage4 にならず ブラウザでも実装されない状況でした
理由はタイムゾーンやカレンダーの標準化待ちで Temporal 自体の API に変更予定はないものの これが終わるまではブラウザでフラグ無しに使えるようにしてはいけないとされていました
この標準化は IETF という別のところでやってるようで それに時間がかかってるようでした

その作業も先週完了しドキュメントが承認されたようです
関連する issue もクローズされました
https://github.com/tc39/proposal-temporal/issues/1450

あとは実装待ちですが 構文が変わるものでもなく polyfill が存在するものなのでもうすぐ使えるようになりそうですね
Yarn4 が出た
正式リリースされたようです
rc は 53 まであったようです

大きな変更はゼロインストール機能がデフォルトで無効になってるようです
ゼロインストールはそれ自体の ON/OFF はなくて PnP とオフラインミラーの両方を使うことで実現できるものです
PnP は Yarn4 でもデフォルトで有効なので オフラインミラーがオフになったようです
具体的には .yarnrc.yml の設定の enableGlobalCache のデフォルト値が true になりました

enableGlobalCache が true だとインストールするパッケージは 各プロジェクトのフォルダ内に保存されず全部のプロジェクトで共通になります
Linux 環境だと 「~/.yarn/berry/cache/」 に入ります
同じパッケージを複数のプロジェクトで使うときも実体はひとつなので効率的です
なのでデフォルト挙動としてはこっちで良いと思います
false にすると各プロジェクトフォルダに保存されるので Git にコミットしてゼロインストールにできます

他には yarnPath の代わりに Corepack が推奨されるようになったようです
とは言ってもまだ yarn init で作られる .yarnrc.yml に yarnPath の設定が記載されています
package.json には packageManager が追加されるようになっています

{
...
"packageManager": "yarn@4.0.0",
...
}

Constraints 機能にも変更があって JavaScript で書けるようになったようです
これまでは Prolog でしたが なぜ Prolog という状態でしたからね
記載する内容に適した言語なのでしょうけど Yarn を使うユーザー層を考えると全然違う言語過ぎて難しすぎました
気軽に使えるようになったのは良いですね
Chrome 119 で select タグに hr が使えるようになる
来週に Stable リリースされる Chrome119
新機能に地味だけど便利かもしれない機能があった

select タグに hr タグを入れて横線を表示できるようになる

<select>
<option>Option1</option>
<option>Option2</option>
<hr/>
<option>Option3</option>
<option>Option4</option>
<hr/>
<option>Option5</option>
<option>Option6</option>
</select>

これまでは option 以外の関係無いタグを入れても自動で削除されてた
Chrome 119 からは hr タグのところに横線が表示されるので 区切りをわかりやすくできる

ただ 見た目はあまりきれいじゃない
スタイルに height や margin などを設定しても無視される
hr タグの有無しか反映されてなさそう
hr タグを連続して入れることで太い線にできるけど 見た目のためにそれはどうなのって思う
Office に月額プランがあった
Office は普段全く使わないので PC を買うときは付属してないのを選んでます
たまにつかうときも OneDrive から開ける無料のウェブ版の Excel などを使ってます
場合によっては Google の方の Spreadsheet だったりです

ただ 今回印刷したり PDF にしたり他の人と共有したりがあります
ウェブ版でも印刷や PDF 化はできるようですが 少し不安があります
こういうときは安定のデスクトップ版にしておきたいです

短期間なのでサブスク版の Office365 を 1 ヶ月だけ契約できたりしないかなと探してみたら 月額払いもありました
現状では月 1490 円です
年だと 14900 円なので 10 ヶ月分

1 年使うのだと 1 年分で買ったほうが安いですが 1 ヶ月や 2 ヶ月程度ならこっちの方がいいですね

https://www.microsoft.com/ja-jp/microsoft-365/buy/compare-all-microsoft-365-products
Node.js 21 がリリースされた
Node.20 の LTS 化は来週みたい

Node.js 21 の新機能
https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V21.md#21.0.0

ブラウザ互換の WebSocket クライアントが追加されたみたいです
まだ実験的なのでフラグが必要です
Node.js をクライアントとして使いたいケースはパッと思いつかないですが テストには便利かも

fetch が stable になったみたいです
18 からフラグなしで使えてたのでもう普通に使ってましたが そういえばまだ experimental でしたね

glob 機能が追加されたみたいです
ただテスト関連の機能で内部的に使ってるだけみたいで公開されてないので fs モジュールをインポートしても直接使うことはできないようです
glob 機能だけでも使えるようになるといいですね

一番気になるところは --experimental-default-type オプションの追加
これでデフォルトを ESM に変更できます
これまではコマンド実行時に ESM と指定できず 拡張子を .mjs にしたり package.json を作って type に module を指定しないといけなくて面倒でした
これで ESM が使いやすくなる と思ったのですが これには問題もあるようです
ここのサイトで議論されている内容など詳しく説明されてました
https://jser.info/2023/10/18/node.js-roadmap-esm-by-default/

自分のコードの範囲だけ考えてましたが デフォルトが変わると node_modules のパッケージにも影響があります
古いパッケージは package.json に type が記載されていなくて CJS で書かれているので デフォルトが ESM になるとエラーになるということみたいです

たしかにそうですね
でも 現状で困っていてデフォルトを変えたいのは package.json を使わないスクリプトを実行するときです
npm パッケージは使わず 自分で書いたモジュールを読み込むときです
対象を package.json が無いところだけ にして node_modules の中の package.json があるところはこれまで通りで type を見て 無ければ CJS でいいと思うんです

package.json を作るところなら type: module の記載をするようにすればいいですし パッケージマネージャーなどが package.json を作るときにデフォルトで type: module になっていれば不便に思うところも無い気がします

package.json があって type が無いものまで ESM にするのだと 過去パッケージは使えないことにするしかないですし 中身を見て判断するというのはパフォーマンスが悪くなるなどが理由で .cjs と .mjs の拡張子に分けると決まったときに拒否されてたはずです
StackBlitz の Node.js のサーバーフレームワーク
StackBlitz で Backend 側テンプレートを見てると 見慣れないものがありました

Egg.js
https://github.com/eggjs/egg

Feathers
https://github.com/feathersjs/feathers

H3
https://github.com/unjs/h3

Nitro
https://github.com/unjs/nitro

ラインナップには Express や Koa はあるのに Hapi や Fastify はありません
それらよりもここにあるのは有名なのでしょうか?

ただ Egg.js と Feathers は言われてみると結構前にも見た覚えがなんとなくあります
2018 年か 2019 年くらいにフレームワークを探してたときだったかもです

たしか Egg.js は Koa を中で使ってて 中国でよく使われてるやつだったかと思います
Feathers はリアルタイム系らしいので WebSocket 系のようです
ソースコードを見てみると Socket.IO を使っていました

今でも StackBlitz のテンプレートに並ぶくらいには使われてるのでしょうか

H3 と Nitro は聞き覚えがないので新しいものかもしれません
リリース履歴をみるとどちらも去年からみたいなので新しいようです

H3 の方は Minimal H(TTP) framework らしく 小さめのライブラリみたいです
ミドルウェア系で Express/Koa に近い感じです
Router がついてるので Koa よりは高機能かもしれません
Express と Koa の違いみたいにハンドラで受け取るオブジェクトが異なっています
ただ Express と互換性のある形にもできるそうです
そうしないと Koa があまり流行らなかったみたいなことになりそうですからね

Nitro の方は Nuxt 関連のもののようです
Nuxt を強化できるらしいですが Nuxt なしでも使えるようで Nuxt を使う場合と使わない場合で設定の書き方が分かれてました

どれも今のところは特に使うことなさそうです
Effector
Stackblitz のテンプレートを見てると Effector というのを見つけました
ビジネスロジックを記述するためのライブラリだそうです
フレームワーク依存ではなく React/Vue/Solid で使えるようです

CounterEffects の例をみるとわかりやすいですが ロジックをコンポーネント外に書くことができて コンポーネントがスッキリするのがいいところみたいです

たしかにロジックはコンポーネントとは切り離したいですしね
でも実際はコンポーネント外でロジックを書くと面倒も多かったりします
フックで得られる値を参照できないので全部引数で渡す必要があったり
そのまま渡すとフックなどの API にロジック側が依存するので それを避けるなら間の層を設けないといけなくなったりします
なのでコンポーネント内に全部書いてしまうこともけっこうあって コンポーネントが大きくなりすぎて見づらい ってなったりします

このライブラリではその問題を解決してくれるようです

ですが 簡単な例を見る限りでは良さそうかも?と思うくらいでしたが複雑な例になってくるとコードもわかりづらくなってきます
そういうのを見てるとそこまでいいのだろうか?と疑問にも思えてきます
このライブラリを使うコード自体がパッと見てすごくわかりやすいとかシンプルにかけそうというのではなく 前提知識がないと難しそうなものですし

もっと知名度が出てユーザーも多くなってくれば考えますが現状だと使わないかなというところでした
Iterator helpers が使えなくなった
Chrome 117 でリリースされた新機能の Iterator helpers ですが 早速ウェブ互換の問題が起きたとかで削除されました

https://github.com/tc39/proposal-iterator-helpers/issues/286
https://bugs.chromium.org/p/chromium/issues/detail?id=1480783

すでに Chrome の最新版では動かなくなっています
Edge ではまだ動きますが 近いうちに削除されると思います

配列のグループ化のときもでしたが またかと言う感じですね
将来的に機能が追加されるなんてわかってるはずなので それを考慮してないようなサイトは相手しなくていいと思うんです
というか ウェブ互換をずっと気にしてると毎回こういう事が起こります
本当 そろそろ互換性無視で新機能追加していったり破壊的変更をしてもいいと思うんです

最低限 「組み込みの prototype には触れるな」 みたいな 将来の機能追加で影響を受けそうな部分に関する◯◯してはいけないというルールを定めて それを無視してもいいけど無視した結果壊れても 対応しないくらいなことはしたほうがいいと思います

配列のグループ化は結局期待してたのとは別の API になるということで解決になりましたが Iterator helpers は使い方変えないでもらいたいですね

今月の TC39 のミーティングの議題に上がっているので そこで方針を決めるのかもですね
https://github.com/tc39/agendas/blob/main/2023/09.md
Chrome 117 で JSON を開いたときにフォーマット表示する機能が増えてた
新機能一覧に載ってなかったので気づいてませんでしたが Chrome で JSON ファイルを直接開いたときにヘッダーが表示されるようなっていました
ヘッダーのメニューには 「プリティ プリント」 のチェックボックスがあります

これにチェックを入れると JSON がきれいにフォーマットされて読みやすくなります

以前はこんなのなかった気がすると思って Chrome 116 で開いてみるとありませんでした
Chrome 117 の新機能のようです
これまでは JSON のフォーマットのために VSCode に貼り付けたり devtools のレスポンスで表示したりしていたので この機能があると便利になりますね

ちなみに Edge は 117 でもこの機能はありません
というか Edge ではデフォルトできれいにフォーマットされて色付けもされています
生のレスポンスを見たいときに Ctrl-U でソースコードを表示したり devtools のレスポンスで確認しないといけないです
これはこれで少し不便だったりもします

ちなみに JSON しか確認していませんが他のファイル形式でもフォーマット表示してくれるのかもしれません
CSS の position:sticky で固定されたときだけスタイルを当てれるようになる
Chrome の現バージョン 116 で DevTrial です
https://chromestatus.com/feature/5072263730167808

詳しい説明
https://lilles.github.io/explainers/state_container_queries.html

position: sticky でスクロールしたときに要素を簡単に固定できるようになりましたが 固定されてるときだけスタイルを変えたいということはよくありました
それができるようになります

使い方はコンテナクエリを使って

@container state(stuck: top) {
#sticky-child { font-size: 75% }
}

のようになるそうです

HTML の例だと

<style>
#sticky {
container-name: my-menu;
container-type: sticky;
position: sticky;
top: 0px;
height: 100px;
}

#sticky-child {
background-color: orange;
color: white;
height: 100%;
}

@container my-menu state(stuck: top) {
#sticky-child { width: 50%; }
}
</style>
<div id="sticky">
<div id="sticky-child">
Sticky
</div>
</div>

できるようになるのは嬉しいのですが 少し手間なのが難点ですね
コンテナクエリが必須ですし contaner-type の指定も必要です

お手軽に擬似クラスで :stuck でいいと思うのですが パフォーマンスの懸念など色々あってコンテナクエリになったようです
CSS に相対カラーの構文が追加されるみたい
CSS Relative Color Syntax (RCS) というのが Chrome 118 で DevTrial のようです
https://chromestatus.com/feature/5205844613922816
https://www.w3.org/TR/css-color-5/#relative-colors

こういう感じです

div {
background: rgb(from var(--bg-color) r g b / 80%);
}

rgb や hsl などの色空間の関数に from で元になる色を指定してから 次に各値を指定します
構文的には

rgb(from 元の色 R G B / 透過率)

rgb 関数なので R と G と B を指定します
透過率は省略可です

R と G と B には これまでの rgb 関数のように 0~255 の数値や%で指定できます
相対と言いつつもこの%は 100% が 255 に相当するもので 元の色に対する割合ではありません

rgb(from red 100% 100% 100%)

と書けば rgb(255, 255, 255) = #ffffff と同じです
red は意味のないものになります

from の色を使うには 数値と%以外の特殊なもので r と g と b を使います
これを使うと元の色の R と G と B の値になります

rgb(from red r g b)

と書くと元の色そのままです

rgb(from red r r r)

と書くと G と B も R の値 (255) になるので rgb(255, 255, 255) です
元の色を使って計算するときは calc を使います

rgb(from rgb(100,100,100) calc(r * 1.1) calc(g * 1.5) calc(b * 2))

これの結果は rgb(110, 150, 200) になります
足し算で直接数値を足したい場合は小数値で扱うことになります
1 が 255 に相当します

rgb(from rgb(100,100,100) calc(r + 0.5) calc(g + 0.1) calc(b + 1))

これの結果は rgb(228, 126, 255) になります
0.5 は 128 なので 100 + 128 = 228 です
1 を足すと 255 を超えてしまうので 255 になります

rgb で使うよりも hsl で hue だけを変えたいみたいなケースのほうが使うかもですね
Chrome 118 で HTML の search 要素に対応するみたい
https://chromestatus.com/feature/5126108151808000

以前話題になっていた HTML の search タグですが Chrome 118 から対応するみたいです

機能としては特になくアクセシビリティのためのものらしいです
「<div role=search>」 と書くのと同じらしいです

検索ボックスが表示されたりはしないので アクセシビリティ系を気にしない人にとっては 無視していいタグです
main とか aside とか header とか nav とか こういう系統のタグです
コンストラクタを見ても main などと同じで HTMLElement になっています

document.createElement("search").constructor.name
// 'HTMLElement'
zstd という圧縮アルゴリズムを知った
ブラウザとサーバー間でデータを送るときに圧縮するアルゴリズム
有名なのは gzip です
brotli が出てきてからもファイルサイズを示すときによく minify+gzip のサイズが書かれているのを見ます

それ以外に新しいものは特に聞いたことがなかったのですが つい先日 zstd というのを知りました
正式名は Zstandard
Z なので zip 系の標準を目指してるということでしょうか
Meta 製のようです
相変わらず Meta って開発者向けなものを色々作ってますよね
Facebook とかの SNS サービスよりこっちがメインの企業じゃないのと思うほど

Wikipedia によれば zstd は
2016 年に 1.0.0 リリース
2018 年に RFC8478
2021 年に RFC8878

のようで できたばかりの新しいものというわけではないようです
全く知らなかったですけど

ブラウザで使えるなら聞いたことくらいありそうだけど と思って調べてみるとまだブラウザでサポートされていないようです
Chrome では 118 (10/11,12 リリース) で使えるようになるそうです
https://chromestatus.com/feature/6186023867908096

後から追加でサポートするものですし brotli より圧縮率や速度が優れてるのでしょうか?
比較してるサイトを見てみましたが 圧縮率やサイズによって変わってきて 常に zstd の方が良いというわけではなさそうです
yarn の公式サイトが新しくなってた
久々に開いたら新しくなってました
https://yarnpkg.com/
綺麗なイラストもあっていい感じですね

yarn4

ドキュメントのサイトが新しくなるということは大きな更新でもあるのかなと見てみると新しいサイトは yarn4 をカバーしてるということみたいです
まだ rc ですが (といっても rc50) 近々リリース予定なのかもです

これまでの古いサイトは別ドメインになってました
https://v3.yarnpkg.com/

メジャーアップデートで大きく変わるのかなと思いましたが ドキュメントを軽く見た感じでは いつもの yarn2 系のようです
3 からの移行についてのページが見当たらないですし 影響の大きい変更はないのかも

CHANGELOG.md を見てみても一般ユーザー向けだと Node.js の古いバージョンのサポート終了やデフォルト値の変更や UI 改善や高速化などが主のようです
Biome.js の v1 が出たらしい
Biome というのを見かけました
v1 が出たらしいです

でもこれ何?
そう思って公式サイトを見てみたものの これはなにかという説明がなくいきなりインストールや使い方の説明から
https://biomejs.dev/

サイドバーなどの項目を見た感じはツールがいくつかまとまったもので Lint とかフォーマッターとかがあるみたいです
Rome みたいなやつかな?

そういえば最近見ないけど Rome ってどうなったんだろう?と思って Rome のリポジトリを見に行きました
https://github.com/rome/tools

もうメンテナンスされなくなっていて後継が Biome なんだとか
似てるどころか後継そのものだったようです

Biome のブログの記事として経緯とか書かれてました
https://biomejs.dev/blog/annoucing-biome

Rome ってよくある普通の OSS だと思ってましたが 最初は Meta 傘下の OSS だったんですね
それから Meta から抜けて Rome を開発するための会社を作ったけど その後会社は失敗に終わったみたいです
Biome 作者の人はその会社の 1 社員で その後も Rome に OSS 活動を続けていたけど権限がなくてできることも限られてるので新しいものにしたということのようです
unload イベントが将来的に削除予定らしい
https://chromestatus.com/feature/5579556305502208

Chrome 117 で Developer trial として unload イベントを削除するための取り組みが始まるようです
まずは unload イベントリスナを追加するための Permission-Policy を追加するみたいです
最初はデフォルトで許可されていて 徐々にデフォルトで拒否に移行していくそうです
そして最終的に unload イベントのサポートを削除するのが目的だそうです

理由は現状でちゃんと動いてなくて信頼性が低いかららしいです
ブラウザによっては動かないものもあり 環境次第となっていて実質標準とは言えない状態みたいですね

特に BFCache との相性が悪いです
BFCache が有効だとページ遷移でページが表示されなくなっても復元される可能性があるので unload されたとは言えないです
それでページ遷移のときに unload を呼び出さなければ そのままそのページに戻って来ないと永遠に unload されないです
なので Chrome は unload リスナがついていたら BFCache は無効になるような挙動だったはずです

Safari は以前からずっと unload イベントが起きないです
以前これで困ったことがあります
でも Chrome だけでも動いてほしいことがあったので 削除されるのはやっぱり困りそうです

代替候補はやっぱり pagehide でしょうか
beforeunload もありますが ユーザーによるキャンセルが可能なので キャンセルされるかもしれないです
pagehide はタブを閉じたり リロードしたり 進む戻るで移動をしたり 新規にナビゲーションしたりでページが見えなくなったときに起きるイベントです
BFCache などで復元されるされない関係なしです
タブの切り替えや ウィンドウの最小化で見えなくなる場合はイベントが起きないです
基本はこれで良さそうですが unload ではないので JavaScript の状態がそのままで復元される可能性があるというのは気をつけないといけないです
あとは pagehide の理由としてリロードとかタブを閉じるとか戻るとかが受け取れればいいのですけどね

const win = window.open(location.href)
win.addEventListener("load", () => console.log("event load"))
win.addEventListener("unload", () => console.log("event unload"))
win.addEventListener("pageshow", () => console.log("event pageshow"))
win.addEventListener("pagehide", () => console.log("event pagehide"))
event pagehide
event unload
event load
event pageshow
(開いたタブを閉じる)
event pagehide
event unload
Chrome 118 で @scope 機能が追加される予定
https://chromestatus.com/feature/5100672734199808
https://drafts.csswg.org/css-cascade-6/#scope-atrule

CSS に @scope という機能が増えるようです

@scope (.foo) to (.bar) {
a { color: red; }
}

と書けば DOM のツリー上で .foo の内側で .bar の外側を対象に a タグの色を変えるということになります
ShadowDOM みたいなスタイルのスコープ機能が追加されるということですね
WebComponents を使うなら ShadowDOM でいいですが React 等のフレームワークの場合はコンポーネントごとに ShadowDOM を使わないのでスコープを制御し辛いです
なのでこういうのがあると便利になりそうです

これに合わせて Scope Proximity という新しい概念が追加されてスタイルの優先度にも変更があるようです
詳細度が同じ場合 セレクタの対象要素とスコープのルート要素間の距離が近いほど優先度が高くなります
スコープが指定されないこれまでのスタイル定義だと距離が無限(優先度最小)として扱われるようです

例えばタブがあって その中にフォームがあって タブとフォームにスコープが設定されていて フォームパーツは両方のスコープに入る場合 まずフォームスコープのスタイルが優先されて次にタブスコープのスタイルで 最後にページ全体のということのようです
優先度的には良さそうです
ただ詳細度の方が優先度が上なので やっぱり詳細度には悩まされそうです

構文的には

@scope [(<scope-start>)]? [to (<scope-end>)]? {
<rule-list>
}

なので scope-start や scope-end は省略できるみたいです
scope-end を省略すると

@scope (.dark-scheme) {
a { color: plum; }
}

CSS のネスト機能が使えるようになる前なら ネスト機能としても使えるので需要が高かったのかもしれません
でも今では先にネスト機能が使えるようになっているので あまりこのケースは使わなそうです

ネスト機能と同じようなことができますが 全く同じではなく少しだけ違いがあります
@scope に指定するセレクタ (上の場合の .dark-scheme) の部分は詳細度に含まれません
ネストの場合に詳細度に影響しないよう :where() を使うような挙動です
その場合でもスコープによる優先度があるので :where() を使うより @scope の方が優先度が高くなります

scope-start を省略する場合は その style タグの親要素がスコープのルートになります

<div>
<style>
@scope {
p { color: red; }
}
</style>
<p>ここは赤色</p>
</div>
<p>ここは赤色じゃない</p>

昔 style タグに scoped 属性がありましたが それに近いことができますね
Node.js に Symbol.dispose が増えてた
Node.js 20.4 で Explicit Resource Management の proposal をサポートしたそうです
試してみましたが using は構文エラーでした

リソースの解放が必要な Node.js の API で using に対応しただけで using そのものはまだ使えないみたいです
--experimental 系フラグを見てもそれらしいのはまだなかったです

using を使って解放可能なリソースのオブジェクトは Symbol.dispose や Symbol.asyncDispose プロパティを持っていて 解放処理の関数が入ってます
それらが追加されたということみたいです

対応前のバージョンだと

Welcome to Node.js v18.13.0.
Type ".help" for more information.
> Symbol.dispose
undefined
> Symbol.asyncDispose
undefined

ですが 対応後のバージョンだと

Welcome to Node.js v20.5.0.
Type ".help" for more information.
> Symbol.dispose
Symbol(nodejs.dispose)
> Symbol.asyncDispose
Symbol(nodejs.asyncDispose)
> fs.writeFileSync("a", "")
undefined
> const a = fs.createReadStream("a")
undefined
> a[Symbol.dispose]
undefined
> a[Symbol.asyncDispose]
[Function (anonymous)]

ファイルを読み取る stream では Symbol.asyncDispose に関数が入ってますね

シンボルが nodejs.dispose という名前になってますが V8 エンジン側でまだサポートされてないから Node.js で独自に用意してるだけで 正式に V8 側で対応したら独自のものじゃなくなるのだと思います
popover 属性使ってみた
来週 Stable リリース予定の Chrome 114 で Popover API がリリースされる予定

div など好きな要素に popover 属性をつけれる

<div popover>
あいうえお
</div>

popover 属性をつけた要素は表示されない
UA のスタイルで display: none されてる

[popover]:not(:popover-open):not(dialog[open]) {
display: none;
}

popover 要素は showPopover メソッドで表示できる

document.querySelector("div").showPopover()

dialog と同じ感じで中央に表示される
表示は最上位になるので position: fixed で z-index 大きめの要素があってもさらにその上に来る

popover の要素の外側の適当なところをクリックすると自動で閉じてくれる
プログラムから閉じるときは hidePopover()

属性で popover="manual" を指定すると自動で閉じなくなる
デフォルトの value 省略形は popover="auto" を指定するのと一緒

popover 属性がついてない要素に showPopover を実行するとエラーになる

Failed to execute 'showPopover' on 'HTMLElement': Not supported on elements that do not have a valid value for the 'popover' attribute.

表示中に showPopover を呼び出してもエラーになる

Failed to execute 'showPopover' on 'HTMLElement': Invalid on popover elements which aren't hidden.

この辺はもっとゆるくていいと思うのに
togglePopover() メソッドもあって 引数の true/false で表示・非表示を指定できる
こっちだと表示中に表示しようとしてもエラーは出ないのでこっちのほうが扱いやすいかも

複数の popover 要素があって 表示中の要素があるときに 別の要素を表示させようとした場合は showPopover でエラーにならない
すでに表示していた popover 要素は自動で閉じられる
popover="manual" の場合は閉じられないので 複数の popover 要素が表示できる

popovertarget と popovertargetaction 属性もあって これを使うと JavaScript なしで HTML だけでも表示・非表示を制御できる
属性名が長い

<button popovertarget="foo" popovertargetaction="show">
ここをクリックすると POPOVER を表示
</button>

<div popover id="foo">
<h1>POPOVER</h1>
<button popovertarget="foo" popovertargetaction="hide">
ここか外側をクリックすると POPOVER を隠す
</button>
</div>

マニュアルの場合

<button popovertarget="foo" popovertargetaction="show">
SHOW
</button>
<button popovertarget="foo" popovertargetaction="hide">
HIDE
</button>
<button popovertarget="foo" popovertargetaction="toggle">
TOGGLE
</button>

<div popover="manual" id="foo">
<h1>POPOVER</h1>
</div>

popover のネストもできる
popover で表示される要素の中に popover 要素を配置する

<button popovertarget="foo" popovertargetaction="show">SHOW</button>

<div popover id="foo">
<h1>POPOVER1</h1>

<button popovertarget="bar" popovertargetaction="show">SHOW</button>
<div popover id="bar">
<h1>POPOVER2</h1>
</div>
</div>

POPOVER2 を表示すると POPOVER1 は後ろで表示されたまま
POPOVER2 の外側かつ POPOVER1 の内側のエリアをクリックすると POPOVER2 だけを閉じれる
POPOVER1 の外側をクリックすると両方とも閉じられる

現状の機能だと位置は中央にしか来ないみたい
表示するボタンのすぐ下に持ってきたりする機能はなさそう
中央に来てるのはデフォルトの UA スタイルで margin: auto だからなので margin 等を JavaScript で調整して期待の位置に持っていくことになりそう

中身まで見てないけど Chrome 116 の機能で CSS Anchor Positioning というのがあるので これでアンカーを指定できるようになるのかも?
hhvm のリリースが止まってた
久々に hhvm の更新を見に行きました
以前は頻繁なペースで更新されていたのに 更新が止まってました

https://hhvm.com/blog/

2022/11/02 に HHVM 4.172 がリリースされて 次の 11/14 の記事では 4.173 をスキップしてリリースプロセスを再検討すると発表されていました
数週間のうちに次の発表をするつもりらしいのですが 特に発表はなく半年ほど経っています
一応その間でも CVE 対応のリリースはしているようです

Github のリポジトリを見てみた限りでは頻繁にコミットされていますし プロジェクトが終わりそうとか言うことはなさそうです
ただ issue がほとんどなく Facebook 以外の一般の開発者で使ってる人は少なそうです

hhvm が出た頃は PHP が遅いとか使いづらいとか色々ありましたが それ以降 PHP は大きく改善していってますし 最近では hack/hhvm の話題はほぼ見なくなってますし
特に何年か前からは PHP との互換性をなくして独自の方向に進んでいってるので さらにユーザーが減ってるのかもしれません

外部の人がほぼ使っていなくて内部利用のみだと正式なリリースみたいなのも手間になるのでこのまま放置される可能性もありそうですね
display: none をアニメーションできるようになるらしい
https://chromestatus.com/feature/5154958272364544

これまでだと display: none があるとアニメーションできず すぐ消えてしまっていました
それが Chrome 115 でアニメーションできるようになるみたいです
徐々に薄くなったり小さくなったりするわけではなく 完全に none になるまでの間は表示されてる扱いで その他のアニメーションが有効になるというものです
薄くしたり 小さくしたり は自分で別の CSS プロパティをアニメーションさせて実装する必要があります

これまでだと そういうアニメーションを実行させてから 終わったのを JavaScript で検知して display: none にする必要がありました
今回の変更で アニメーション後に自動で display: none になるので少し楽になるという感じです

個人的には display: none ではなくて remove してしまいたいことのほうが多いので意外と使い所がなかったです
画面端に出てくる通知みたいなものだと 任意個数になるので事前に要素を作っておかずに その場で要素を作って使い終わると削除しますからね
ずっと実体が残ったままだとポップアップやダイアログでしょうか


少し使った感じだと

@keyframes hide {
0% { height: 100px; display: block }
100% { height: 0; display: none }
}

div {
height: 100px;
background: red;
}

.hidden {
animation: hide 2s linear 0s;
display: none;
height: 0;
}

のように .hidden に非表示状態のスタイルも書いておくとアニメーションしてくれませんでした
非表示にするときに hidden クラスをつけます

他のスタイルだと アニメーション中はアニメーションのスタイルが優先されて アニメーションが終わると直接指定されたスタイルに戻るというものです

@keyframes color {
0% { color: red; }
100% { color: pink; }
}

.color {
animation: color 2s linear 0s;
color: blue;
}

だと color クラスをつけると 赤からピンクに文字色が変わり アニメーションが終わると青色になります
そう考えると .hidden に display: none があっても問題ないように思うのですが .hidden の中に display: none があるとアニメーションされないです

display: none をキープするには アニメーション後にスタイルを保持する設定が必要です

.hidden {
animation: hide 2s linear 0s forwards;
}

のように forwards を指定するとアニメーションの最後のフレームのスタイルのままになります
でも直接書かれたスタイルじゃないので分かりづらく好きじゃない方法です
100% と同じ内容をスタイルに直接指定して動くようなって欲しいですね
CSS の zoom プロパティがなくなるらしい
https://chromestatus.com/feature/6535859207143424

非標準で Firefox だとサポートされてないものみたいです
event.path のときと同じ感じみたいですね

実際ズームしたいときは transform の scale 関数や scale プロパティで指定していたので zoom は特に使った覚えもないです

.zoom {
transform: scale(1.2);
/* or */
scale: 1.2;
}

代替があるなら無くても良さそうと思いましたが 全く同じ挙動ではないみたいで zoom を正式な機能にしたいという要望もあるようでした
違いは拡大のタイミングで レイアウトの前にズームするか後にズームするかが異なってるようです

scale だとレイアウト計算後の拡大・縮小なので 倍率が変わっても実体の大きさは変わらないとして扱われて 周りのレイアウトを壊しません
なので アニメーションに向いています
ですが レイアウトが決まった後に拡大するので 画面幅 100% いっぱいのページに対して全体を拡大するとはみ出ます
スクロールバーが出てきます
そういうことがあるので レイアウトを計算する段階で拡大を有効にしたい場合には使えません

これに対して zoom だと拡大・縮小されたものとしてレイアウトが計算されるのでスクロールバーは出ません
固定サイズのものを折り返しなしで並べるなど スタイルによっては出るケースもありますが scale と違って確実に出るわけではないです
Ctrl-+ などブラウザの機能としてのズームと同じ扱いです

こういう違いがあると zoom がなくなると困る人もいそうです
Safari (Webkit) もこの機能を実装していて Chrome に合わせて削除するかの議論がこの issue です
https://github.com/WebKit/standards-positions/issues/170

壊れるページが出てくると言ってますし 本当に削除できるのか疑問もあります
Chrome も壊れるサイトが多いとやっぱりやめるとかあるのかもしれません
現状の予定では Chrome 114 (次のバージョン) で Developer trial として削除されて 117 で正式に削除の予定のようです

また zoom には現在の倍率を知るという使い方もあったようで これができなくなるそうです

getComputedStyle(document.documentElement).zoom
// '1'

のように html 要素の計算済みスタイルの zoom を参照します
Ctrl-+ などブラウザの機能でズーム倍率を変更して 150% にすると '1.5' になります

Windows の機能で拡大縮小した場合も反映されます
150% 設定なら '1.5' になります
Windows の設定で 150% で さらにブラウザで 150% にズームすると掛け算されて '2.25' になります

倍率がわかって便利です

しかし html 要素に zoom スタイルがあたってると この機能がなくなります
スタイルとして指定された値が固定で返ってきます

html { zoom: 2 }

というスタイルがあると Windows の設定やブラウザのズーム倍率は無視され ずっと '2' になります
また Windows の設定で 150% なら デフォルトが '1.5' のはずなのに

html { zoom: 1.5 }

というスタイルを適用すると そのままの倍率ではなく さらに 150% の拡大が行われます
非標準というだけあってわかりづらく一貫性のない挙動です

設定せず参照のみであれば使えそうな気はしますが 現在は window.devicePixelRatio で同じ値が取れるのでこれはなくてもよさそうです



その後
Chrome 113
Chrome 113 の stable リリース日が今日なので そろそろリリースされそうですね

112 の CSS Nesting に比べるとあまり気になる追加機能のないバージョンですが WebGPU は一部開発者の人には嬉しい機能なのかもです
個人的には Canvas の WebGL ですら使わないので 使うことはなさそうですけど
ゲームとか AI 処理で活用されるのでしょうか
アクセスにユーザー許可がいらないなら以前流行ったマイニングに利用されてまた流行ったり?

他には CSS 機能がいくつか増えてるようです
メディア機能 (@media) に update と overflow-block と overflow-inline が追加されます
update では 更新される画面 (PC) か更新されない画面 (印刷) かでスタイルを変えられるようです
電池残量が少なく画面の更新頻度が低い場合や 電子書籍リーダーみたいな画面更新が遅い環境向けの更新できるけど遅いというパターンも指定できるようです
overflow では はみ出たときに見えないか スクロールできるか ページに分かれるかなので これも画面か印刷かで分かれるようです

また animation の easing 関数で linear 関数が追加されてます
linear とか ease-out みたいに指定していたところで linear(0, 0.25, 1) みたいなものをかけるようになります

JavaScript では fetch の Header に getSetCookie メソッドが追加されます
fetch では Cookie を扱えません

fetch(url, {
headers: {
cookie: "foo=1; bar=2",
},
})

と書いても cookie は送信できませんし

const res = await fetch(url)
console.log([...res.headers.entries()])

と書いても 取得できるヘッダー情報に set-cookie が含まれずサーバーから保存するよう指定された Cookie の情報が見れません

セキュリティ上 仕方ないのかなと思いますが 今回の更新では set-cookie ヘッダーの情報が取れるようになるようです

const res = await fetch(url)
console.log(res.headers.getSetCookie())

getset って続く分かりづらい名前です

set-cookie の情報を取れたところでセットはできないですし あまり使いどころが思いつきません
fetch のオプションでは渡せませんが 現ドメインへのリクエスト限定なら document.cookie を書き換えれば送ることはできます
ただ set-cookie が返ってきてたら自動でセットされてるはずですし 意味がないです
そのまま使わず書き換えるにしても HttpOnly があると自動でセットされたデータを消せません
credentials を omit にすれば set-cookie で Cookie が保存されないので JavaScript でセットできるかもです
ですがそもそも現ドメインに限るなら サーバーを自身で管理してるはずなのでサーバー側で対応すればよく JavaScript で set-cookie を取得したいとなることもなさそうです

最近はサーバーサイドでも fetch が使われるので Node.js や Deno 向け仕様かなと思いましたが それなら送信時のセットもできないと不便です
試してみると Node.js 18 の LTS では Cookie が特別扱いされておらず set-cookie は headers のデータに含まれていました
やっぱりなんのために追加されたのかよくわからない機能です



気になったのでもう少し調べてみると Node.js などサーバー向け機能であってたようです
https://developer.mozilla.org/en-US/docs/Web/API/Headers/getSetCookie

set-cookie で複数の Cookie をセットする場合 HTTP header に set-cookie が複数回現れます
しかし ヘッダーのデータを JavaScript で取得するときには 1 つの文字列しか取得できません
Headers には URLSearchParams の getAll みたいなメソッドは無いようです
なので複数の set-cookie が 1 つにまとめられるのですが 「,」 区切りになります
期限の日付表記など 1 つの Cookie の情報のなかにも 「,」 は現れることがあるのでパースが大変です
「,」 で split するような簡単な方法で済ませられません

そこで getSetCookie があると便利で配列で取ってこれます
最初から MDN 見てればよかったです

console.log(process.version)
// v20.0.0

const res = await fetch("https://github.com/")

console.log(res.headers.get("set-cookie"))
// _gh_sess=7a略%3D; Path=/; HttpOnly; Secure; SameSite=Lax, _octo=GH1.1.143815131.1683069288; Path=/; Domain=github.com; Expires=Thu, 02 May 2024 23:14:48 GMT; Secure; SameSite=Lax, logged_in=no; Path=/; Domain=github.com; Expires=Thu, 02 May 2024 23:14:48 GMT; HttpOnly; Secure; SameSite=Lax

console.log(res.headers.getSetCookie())
// [
// '_gh_sess=7a略%3D; Path=/; HttpOnly; Secure; SameSite=Lax',
// '_octo=GH1.1.143815131.1683069288; Path=/; Domain=github.com; Expires=Thu, 02 May 2024 23:14:48 GMT; Secure; SameSite=Lax',
// 'logged_in=no; Path=/; Domain=github.com; Expires=Thu, 02 May 2024 23:14:48 GMT; HttpOnly; Secure; SameSite=Lax'
// ]
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 はその辺がゆるくソースコードを変えなくても実行時に無理やり置き換えて対処できるのが良いところでもあるのに それができなくなるのはデメリットでもあるように思います

使う側でその辺のゆるさを設定できればいいのですけどね
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



以上の内容はhttps://let.blog.jp/category/Presentより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14