<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>tsuka-ryu fun blog</title>
    <link>https://tsuka-ryu.dev/</link>
    <description>tsuka-ryuの個人ブログ</description>
    <language>ja</language>
    <lastBuildDate>Fri, 19 Jun 2026 15:58:28 +0900</lastBuildDate>
    <atom:link href="https://tsuka-ryu.dev/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Resilient Parsingについてのメモ</title>
      <link>https://tsuka-ryu.dev/posts/2026-06-19-resilient-parsing</link>
      <guid isPermaLink="true">https://tsuka-ryu.dev/posts/2026-06-19-resilient-parsing</guid>
      <pubDate>Fri, 19 Jun 2026 00:00:00 +0900</pubDate>
      <description>Resilient Parsingについてメモします</description>
      <content:encoded><![CDATA[<p>今回はResilient Parsingについてのメモです。matkladは偉大。</p>
<p>詳細の実装より概念的なところを記録しておきたい気持ちです。</p>
<h2 id="resilient-parsingとは">Resilient Parsingとは</h2>
<p>Resilient Parsingとは「構文エラーがあっても、エラーを収集したり木構造を頑張ってパースする技」だと思いました。</p>
<p>アカデミックな分野ではエラーリカバリという概念？で微妙に違うっぽいです[要出典]。Crafting Interpretersでも<code>panic mode</code>や<code>synchronization</code>みたいに飛ばし読みして復帰する技術はありますが、そこを一歩越えて、壊れた箇所は壊れてるとマークしつつ、木構造は維持するイメージみたいでした。</p>
<p>Resilient Parsingの概念については、matklad氏の<a href="https://matklad.github.io/2023/05/21/resilient-ll-parsing-tutorial.html" target="_blank" rel="noopener noreferrer">Resilient LL Parsing Tutorial</a>が主要な参照元になるようです。自分の能力不足でOpusに質問してやっとちょっと意味がわかったかなという感じでした。</p>
<p>matklad氏の記事を読んだ後に、opusの指示のもとoxcのコードベースに近い<a href="https://github.com/tsuka-ryu/compiler-workshop-rust/pull/7" target="_blank" rel="noopener noreferrer">Resilient parsingを実装</a>したのですが、やってることが結構違いました。</p>
<p>ここではその違いについて見ていきたいと思います。</p>
<p>ただ、すでに<a href="https://github.com/tsuka-ryu/compiler-workshop-rust/blob/main/docs/parse-resilient.md" target="_blank" rel="noopener noreferrer">Opusが書いてくれた解説</a>もあります。</p>
<h2 id="matklad版">matklad版</h2>
<h3 id="resilient-parsingの例">Resilient Parsingの例</h3>
<p>最初に例です。以下のように不完全な式の定義が並んでいる場合でも、rust-analyzerのようなツールではなるべく壊れていないASTが欲しいです。</p>
<pre><code class="language-rust">fn f1(x: i32,                  // 閉じ忘れ
fn f2(x: i32,, z: i32) {}      // カンマ重複
fn f3() {}
</code></pre>
<p>Resilient Parsingで実装すると、以下のような構造にパースされます。</p>
<pre><code class="language-rust">File
  Fn
    &#39;fn&#39;
    &#39;f1&#39;
    ParamList
      &#39;(&#39;
      (Param &#39;x&#39; &#39;:&#39; (TypeExpr &#39;i32&#39;) &#39;,&#39;)
      error: expected RParen // RParenがないことを報告している（実際は木のノードではなく別建ての診断リストに積まれる）
  Fn // 一つ前のFnに閉じ忘れがあっても、ちゃんとFn定義としてパースが開始されてる
    &#39;fn&#39;
    &#39;f2&#39;
    ParamList
      &#39;(&#39;
      (Param &#39;x&#39; &#39;:&#39; (TypeExpr &#39;i32&#39;) &#39;,&#39;)
      ErrorTree
        error: expected parameter // カンマが重複していても、それ以降の引数もちゃんとパースされる
        &#39;,&#39;
      (Param &#39;z&#39; &#39;:&#39; (TypeExpr &#39;i32&#39;))
      &#39;)&#39;
    (Block &#39;{&#39; &#39;}&#39;)
  Fn
    &#39;fn&#39;
    &#39;f3&#39;
    (ParamList &#39;(&#39; &#39;)&#39;)
    (Block &#39;{&#39; &#39;}&#39;)
</code></pre>
<p>Resilient Parsingはいくつかのアイディアの組み合わせです。</p>
<ul>
<li><p>パースを試みるとき、最低１トークンは消費する（たとえエラー報告でも）</p>
</li>
<li><p>平坦なイベント列、ホモジニアスな木、の２段階でパースする</p>
</li>
<li><p>再帰下降パーサーはただのネストしたループ構造であり、パース時の処理は三択で表現される</p>
</li>
</ul>
<h3 id="最低１トークンは消費する">最低１トークンは消費する</h3>
<p>ここは強調されていたので重要な気がする。</p>
<p>パーサーあるあるは、無限ループに陥ってしまうことらしいですが、Resilient Parsingは最低１トークンは消費するのでその心配がない、というかそうならないように設計された概念な気がしました。</p>
<h3 id="平坦なイベント列-ホモジニアスな木-の２段階でパースする">平坦なイベント列、ホモジニアスな木、の２段階でパースする</h3>
<h4 id="平坦なイベント列">平坦なイベント列</h4>
<p>まずはイベントの定義。</p>
<pre><code class="language-rust">// イベント列の定義
enum Event {
  Open { kind: TreeKind },
  Close,
  Advance,
}

struct Parser {
  tokens: Vec&lt;Token&gt;,
  pos: usize,
  fuel: Cell&lt;u32&gt;,
  events: Vec&lt;Event&gt;, // これ
}
</code></pre>
<p>先ほどの例で言うと、パースする前に平坦なイベント列に変換されます。</p>
<p>ここではわかりやすく階層構造で表現してますが、実体はVecにpushされたトークンの集合です。
<code>Open</code> / <code>Close</code>でトークンを囲み、現在のトークン（<code>fn</code>や<code>(</code>や<code>x</code>など）は葉として<code>Advance</code>と色付けされます。</p>
<pre><code class="language-rust">Open(File)
  Open(Fn)                       ← f1
    Advance  fn
    Advance  f1
    Open(ParamList)
      Advance  (
      Open(Param)
        Advance  x
        Advance  :
        Open(TypeExpr)
          Advance  i32
        Close                    // TypeExpr
        Advance  ,
      Close                      // Param
      //── loop: 次は `fn`(=f2) → 回復集合にヒット → break
      //── expect(`)`) : 診断だけ出してイベントなし・前進なし
    Close                        // ParamList   ← `)` を欠いたまま閉じる
    // ── arrow なし / block なし
  Close                          // Fn
  // ...以下略
</code></pre>
<p>この構造の何が嬉しいかというと、Vec上の<code>Open</code>の位置さえわかればあとで簡単に書き換えたり追加したりできるという点です。</p>
<p>matkladの記事中で書かれている例がわかりやすいです。<code>f(1)(2) </code>をパースする場合、<code>Open</code>は<code>(f(1))(2)</code>の左結合でパースしないといけないですが、<code>f(1)</code>まで読んだ時点では<code>(f(1))</code>と自分自身を囲む<code>Open</code>を作ることはできません。</p>
<p>もし木構造であれば、<code>f(1)(2)</code>まで読み終えてから木構造全体を作り直す必要がありそうです。</p>
<p>平坦なイベント列であれば、「これは左結合だ」と気づいたタイミングで、Vecの特定の位置に<code>Open</code>を差し込めば、構造として成立します。matkladの実装ではこれを<code>open_before()</code>というメソッドで行っています（直前の<code>Close</code>の前に<code>Open</code>を遡って挿入する）。</p>
<p>また、壊れた構文を発見したときも、トークンを読んでエラーだと気づいたあとに<code>Open(Error) / Advance / Close</code>だ、ということがわかります。そのため、読み始めるタイミングでは<code>Open(???)</code>で、最後まで読んでから正解を埋め込めるというのが嬉しいです。</p>
<h4 id="ホモジニアスな木">ホモジニアスな木</h4>
<p>ホモジニアス（均質）な木とは、すべてのノードが「種類タグ＋子のVec」という構造のことです。子に何が何個来ても同じように扱います。</p>
<pre><code class="language-rust">// ホモジニアス — ノードは1種類だけ:
enum TreeKind {        // 種類は「タグ」でしかない
    File, Fn, ParamList, Param, TypeExpr,
    ExprCall, ExprName, ExprLiteral, ErrorTree, ...
}

enum Child {
    Token(Token),      // 葉（リーフ）
    Tree(Tree),        // 枝
}

struct Tree {
    kind: TreeKind,    // ← 種類はここ。型ではなくデータ
    children: Vec&lt;Child&gt;,
}
</code></pre>
<p>この利点は「最低１トークン消費する」を実現しやすい点です。Errorがあっても、ErrorTreeとして素直に表現してトークンを消費することが可能です。</p>
<h4 id="２段階でパースする">２段階でパースする</h4>
<p>２段階でパースすることで、イベント列は単純にVecにpushしていき、イベント列をインプットに木に畳み込むだけ、というシンプルな分業が可能になります。エラーがあっても扱いが簡単です。</p>
<p>どちらの実装も複雑なアルゴリズムも必要ありません。</p>
<h3 id="パース時の処理は三択で表現される">パース時の処理は三択で表現される</h3>
<p>matkladの記事を読んでいてわりと感動したのが、パース時の処理が三択で表現される部分です。</p>
<p>一般的に、パーサ（構文解析）の処理は、次のような入れ子構造のループとして機能します。</p>
<pre><code class="language-rust">loop { // 関数のリストを解析する
  loop { // 関数内のステートメント（文）のリストを解析する
    loop { // 式のリストを解析する
    }
  }
}
</code></pre>
<ul>
<li><p>要素のパースを試みる: 正常系。param や stmt や arg を呼ぶ。</p>
</li>
<li><p>予期しないトークンをスキップする: <code>advance_with_error</code> で1個食べて続行(f2 の余分なカンマ)。</p>
</li>
<li><p>ループを抜けて親に回復を委ねる: break(f2 で fn を見たとき)。</p>
</li>
</ul>
<p>f2を例にとると、以下のようなコードです。</p>
<pre><code class="language-rust">const PARAM_LIST_RECOVERY: &amp;[TokenKind] = &amp;[Arrow, LCurly, FnKeyword];
fn param_list(p: &amp;mut Parser) {
  assert!(p.at(LParen));
  let m = p.open();
  p.expect(LParen);
  while !p.at(RParen) &amp;&amp; !p.eof() {
    if p.at(Name) {
      param(p); // 仮引数、型注釈、カンマをセットで消費する
    } else {
      if p.at_any(PARAM_LIST_RECOVERY) { // ① {fn, &#39;{&#39;} を見つけちゃった
        break;
      }
      p.advance_with_error(&quot;expected parameter&quot;); // ② トークン消費を続ける
    }
  }
  p.expect(RParen);
  p.close(m, ParamList);
}
</code></pre>
<ol>
<li><p><code>f2(x: i32,, z: i32)</code>の引数リストをパースするときに、次の<code>fn</code>キーワードなどが出てきてしまったということは、すでに次の関数のパースが始まってしまっていることになります。このままパースすると大半が壊れた木になってしまいます。そのため、いまの関数をパースするloopからは<code>break</code>で脱出します</p>
</li>
<li><p>想定外の値があっても、Errorとして消費します</p>
</li>
</ol>
<h3 id="matklad版のまとめ">matklad版のまとめ</h3>
<p>主要な概念については多分書けた気がします。よくできてて感動しますね。</p>
<h2 id="oxc版">oxc版</h2>
<p>oxc版といっても、Opusがそういっていただけでまだ自分でコードを読んでないので正しいかわかりません。ただ、matkladのResilient Parsingとはだいぶ違うものみたいです。概念に共通点があるだけのようですね。</p>
<p>こちらは<code>typed AST</code>を直接組みながら2層のエラーで回復する方式です。実装としては、いままで<code>panic!</code>で処理していた部分で<code>expect()</code>して、エラーを突っ込んでいく感じです。例を見てみましょう。</p>
<p>本来は末尾に<code>;</code>が必要な<code>&quot;const x = 5&quot;</code>をパースした場合、以下のようなASTになります。</p>
<pre><code class="language-rust">ParserReturn {
    statements: [
        ConstDeclaration {
            name: &quot;x&quot;,
            type_annotation: None,
            init: Number {
                value: 5,
                },
            },
        },
    ],
    errors: [
        ParseError {
            message: &quot;expected &#39;;&#39;&quot;,
        },
    ],
    panicked: false,
}
</code></pre>
<p>あるいは、不完全な構文<code>&quot;const x = ;&quot;</code>をパースした場合、以下のようなASTになります。</p>
<pre><code class="language-rust">ParserReturn {
    statements: [],
    errors: [
        ParseError {
            message: &quot;unexpected token in expression: Semicolon&quot;,
            span: Span {
                start: 10,
                end: 11,
            },
        },
    ],
    panicked: true,
}
</code></pre>
<p>この中で出てくる<code>errors</code>や<code>panicked</code>が2層のエラー回復部分です。</p>
<h3 id="errors-と-panicked">errors と panicked</h3>
<p>それぞれ役割が違います。</p>
<ul>
<li><p>errors: 回復可能なエラーを発見した場合は、この配列にpushして続行、木は残る</p>
</li>
<li><p>panicked: 最上位で木構造を捨て、fatal_errorを報告</p>
</li>
</ul>
<h3 id="dummyでスタックを巻き戻す">Dummyでスタックを巻き戻す</h3>
<p>fatalが起きたとき、「Expressionを返す関数なのに値が作れない」ため困ります。oxcは<code>Result</code>や<code>?</code>でのエラー伝播を使用せず、<code>Dummy::dummy()</code>を捨て値として返す。これでコードがシンプルになります。</p>
<h3 id="resilient-parsingなのか">Resilient Parsingなのか？</h3>
<p>panickedで木を捨ててしまうので、matkladが言う「Resilient parsing means recovering as much syntactic structure from erroneous code as possible.（レジリエントな構文解析とは、エラーを含むコードから、可能な限り多くの構文構造を復元することを意味します。）」とはちょっと違いそうですね。</p>
<p>errorsに詰めていく部分はresilient的な感じでしょうか。</p>
<p>というか、そもそもoxcでは<code>Resilient</code>という言葉は使われてないですね（笑）。Opus君、どうなっとんねん（？）</p>
<h2 id="まとめ">まとめ</h2>
<p>Resilient Parsing、おもしろかったです。</p>
<p>matklad氏のResilient Parsingはrust-analyzerが念頭にあるのでなるべく部分木を残す方向ですが、oxcは静的解析時に強制終了してもそんなに困らないんですよね。なので、それぞれ目的に合った実装になっていると言う感じでしょうか。</p>
<p>oxcのコードがちょっとずつ読めるような気がしてきました。次回はアリーナアロケータ（ボスキャラ）を勉強しようと思います。</p>
<hr>
<p><em>最終更新: 2026年6月19日</em></p>
]]></content:encoded>
      <category>コンパイラ</category>
      <category>パーサー</category>
      <category>oxc</category>
    </item>
    <item>
      <title>ブログをFUNSTACK Staticで作り直したよ</title>
      <link>https://tsuka-ryu.dev/posts/2026-06-15-funstack-blog</link>
      <guid isPermaLink="true">https://tsuka-ryu.dev/posts/2026-06-15-funstack-blog</guid>
      <pubDate>Mon, 15 Jun 2026 00:00:00 +0900</pubDate>
      <description>Next.js + Fumadocsから、RSCベースのFUNSTACK Staticにブログを移行したのでメモ</description>
      <content:encoded><![CDATA[<blockquote>
<p>この記事はOpus4.8で生成してます！</p>
</blockquote>
<p>こんにちは。6月も半ばですね。梅雨らしい天気が続いていて、洗濯物の行き場に困っています。</p>
<p>さて、去年の暮れに<a href="https://tsuka-ryu.dev/posts/2025-12-30-my-blog">個人ブログを作った話</a>を書いたばかりなのですが、性懲りもなくまた一から作り直しました。今回は<a href="https://www.npmjs.com/package/@funstack/static" target="_blank" rel="noopener noreferrer">FUNSTACK Static</a>という仕組みに載せ替えています。記事の中身はそのまま移してきたので、見た目だけ知らないうちに変わっていた、という方もいるかもしれません。</p>
<h2 id="なぜまた作り直したのか">なぜまた作り直したのか</h2>
<p>前回はNext.js + <a href="https://www.fumadocs.dev/" target="_blank" rel="noopener noreferrer">Fumadocs</a>で、これはこれですごく良かったです。デザインも機能も最初から揃っていて、ほとんど書くことなく立ち上がりました。</p>
<p>ただ、ありものが良くできているぶん、自分で手を動かす余地があまり残っていなくて、「盆栽」として育てるには少し物足りなさもありました。せっかくの個人ブログなので、中身を全部自分で把握できるくらい小さく作って、気が向いたときにいじり倒したい。ちょうどRSC（React Server Components）でSSGする構成や、最近元気なRust製のツールチェーンを一度きちんと触ってみたかったのもあって、勢いで作り直すことにしました。</p>
<p>三日坊主の人間がブログそのものを作り直してしまうのは、たぶん現実逃避の一種です。そういうものですね。</p>
<h2 id="funstack-staticについて">FUNSTACK Staticについて</h2>
<p><a href="https://www.npmjs.com/package/@funstack/static" target="_blank" rel="noopener noreferrer">FUNSTACK Static</a>は<a href="https://github.com/uhyo" target="_blank" rel="noopener noreferrer">uhyo</a>さんが作られているフレームワークで、React Server Componentsをベースにした静的サイトジェネレータです。ビルド時に全ページをHTMLとして吐き出すので、<strong>ランタイムにサーバーがいりません</strong>。生成物を静的ホスティングに置くだけで動きます。</p>
<p>嬉しいのは、記事本文やMarkdownの変換をビルド時のサーバー環境だけで描画して、クライアントに送るJavaScriptを最小限にできるところです。インタラクティブにしたい「島」の部分だけがスクリプトになる、という感じですね。</p>
<p>ルーティングは<a href="https://www.npmjs.com/package/@funstack/router" target="_blank" rel="noopener noreferrer">FUNSTACK Router</a>というのが別であって、こちらはNavigation APIベースになっています。普通のリンクを踏むとクライアント側でぬるっと遷移してくれます。Navigation APIを実戦で使うのは初めてだったので、地味に楽しかったです。</p>
<h2 id="rust製ツールチェーンが楽しい">Rust製ツールチェーンが楽しい</h2>
<p>今回いちばん遊べたのがこのあたりです。気づけばビルドまわりがRust製だらけになっていました。</p>
<ul>
<li><p><strong>Markdown変換・全文検索</strong>: <a href="https://www.npmjs.com/package/@ox-content/napi" target="_blank" rel="noopener noreferrer">ox-content</a>。Markdownをmdastに変換するのも、全文検索のインデックスを作るのもこれ1つ。検索はCJK対応のBM25ランタイムが吐き出されて、それだけがクライアントに乗ります。</p>
</li>
<li><p><strong>OG画像生成</strong>: <a href="https://www.npmjs.com/package/takumi-js" target="_blank" rel="noopener noreferrer">takumi-js</a>。記事ごとのOG画像をビルド時に描いてくれます。ヘッドレスブラウザがいらないのが偉い。この記事のOG画像も、サイトと同じトンマナになるように作り直しました。</p>
</li>
<li><p><strong>シンタックスハイライト</strong>: <a href="https://shiki.style/" target="_blank" rel="noopener noreferrer">shiki</a>。ビルド時に色をHTMLへ焼き込むので、ハイライタ本体はクライアントに送られません。light/darkのデュアルテーマにCSS変数で追従させています。</p>
</li>
</ul>
<p>Rustそのものを書いているわけではないのですが、こういう速いツールが裏で動いているのを眺めるのは気持ちがいいですね。コンパイラまわりの勉強も続けているので、いつかは中身も読めるようになりたいところです。</p>
<h2 id="typescriptはネイティブ版に">TypeScriptはネイティブ版に</h2>
<p>型チェックは<a href="https://www.npmjs.com/package/@typescript/native-preview" target="_blank" rel="noopener noreferrer">ネイティブ版のTypeScript</a>（<code>tsgo</code>）に一本化しました。<code>tsc</code>の代わりに<code>tsgo --noEmit</code>で型を見ています。速いし、せっかくなので新しいものを使ってみたい、という軽い気持ちです。</p>
<h2 id="デザインを-余白-の方に寄せた">デザインを「余白」の方に寄せた</h2>
<p>前回はありもののデザインに乗っていたぶん、見た目がいかにもテンプレート然としていたのが、実はちょっと気になっていました。今回は温かい紙の地・墨色の本文・くすませた琥珀のアクセントで、本の余白に書き込みをするような落ち着いた雰囲気を目指しました。</p>
<p>ブランドの脇にちょこんと置いている手描き風のアスタリスク（✳）がお気に入りで、記事の区切りやファビコン、OG画像にも使い回しています。落ち着いてはいるけれど、ちょっとだけ遊びがある、くらいの塩梅にできていたら嬉しいです。</p>
<h2 id="ついでに開発まわりも整備した">ついでに開発まわりも整備した</h2>
<p>作り直したついでに、linter/formatterを<a href="https://oxc.rs/" target="_blank" rel="noopener noreferrer">oxlint / oxfmt</a>に、Git hooksを<a href="https://github.com/evilmartians/lefthook" target="_blank" rel="noopener noreferrer">lefthook</a>に揃えました。push前にフォーマット・lint・型チェック・ビルドが通ることを確認してくれるので、雑にコミットしても後で気づける安心感があります。ファビコンもようやく設定しました。</p>
<h2 id="まとめ">まとめ</h2>
<p>そんなわけで、ブログをFUNSTACK Staticで作り直したという話でした。小さく作れたぶん、これからは気軽にいじれそうで楽しみです。盆栽として、ぼちぼち育てていきます。</p>
<p>例によって三日坊主が顔を出すかもしれませんが、半年に一回でも書ければ御の字、くらいの気持ちでやっていきます。それでは。</p>
<hr>
<p><em>最終更新: 2026年6月15日</em></p>
]]></content:encoded>
      <category>ブログ盆栽</category>
      <category>FUNSTACK</category>
      <category>RSC</category>
    </item>
    <item>
      <title>Pratt Parserについてのメモ</title>
      <link>https://tsuka-ryu.dev/posts/2026-06-07-pratt-parser</link>
      <guid isPermaLink="true">https://tsuka-ryu.dev/posts/2026-06-07-pratt-parser</guid>
      <pubDate>Sun, 07 Jun 2026 00:00:00 +0900</pubDate>
      <description>Pratt Parserについてメモします</description>
      <content:encoded><![CDATA[<p>こんにちは。6月になりました。暑かったり寒かったりで、季節が不明ですね。</p>
<p>今回はPratt Parserについてメモをしておきたいと思います。毎回ふんわりと理解して、ふんわり忘れるため…</p>
<p>自分でした実装（というかほとんどClaudeに書いてもらった）としては、<a href="https://github.com/tsuka-ryu/toy-pratt-parser" target="_blank" rel="noopener noreferrer">typescript版</a>と<a href="https://github.com/tsuka-ryu/compiler-workshop-rust/pull/6" target="_blank" rel="noopener noreferrer">rust版</a>があります。</p>
<p>ちゃんとした情報を知りたい方は、<a href="https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html" target="_blank" rel="noopener noreferrer">Simple but Powerful Pratt Parsing</a>を読んでください。この記事以上の情報はここにはありません。</p>
<h2 id="pratt-parserとは">Pratt Parserとは</h2>
<p>ざっくり自分の理解を書くと、「再帰下降パーサーで優先順位ごとに関数を分けなくても、結合力（binding power）を使えば loop + 再帰でフラットに構文解析できちゃうよ」ということだと思います。</p>
<p>別に再帰下降パーサーを使わなくなるわけではなく、組み合わせて使うこともあるようです。複数の演算子などがある場合に、結合力を定義してあげればうまくいく、というのが嬉しさっぽいですね。</p>
<h2 id="pratt-parserについての参考文献">Pratt Parserについての参考文献</h2>
<p>他にもあると思いますが、自分が眺めた参考文献を列挙しておきます。</p>
<ul>
<li><p><a href="https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html" target="_blank" rel="noopener noreferrer">matklad - Simple but Powerful Pratt Parsing</a>: とにかくこれを読めばよし</p>
</li>
<li><p><a href="https://www.moonbitlang.com/pearls/pratt-parse" target="_blank" rel="noopener noreferrer">MoonBit Pearls Vol.01：Implementing a Pratt Parser in MoonBit</a>: 一番最初のブログがこれなんだ〜（小並感）</p>
</li>
<li><p><a href="https://eli.thegreenplace.net/2010/01/02/top-down-operator-precedence-parsing" target="_blank" rel="noopener noreferrer">Top-Down operator precedence (Pratt) parsing</a>: 比較的短め</p>
</li>
</ul>
<p>以下では、実際に使われています。</p>
<ul>
<li><p><a href="https://github.com/acornjs/acorn/blob/master/acorn/src/expression.js" target="_blank" rel="noopener noreferrer">acorn/src/expression.js</a>: Pratt とほぼ同型の優先順位パーサー（operator precedence parser）を使っています</p>
</li>
<li><p><a href="https://github.com/oxc-project/oxc/blob/388af9d7d577424292931eede19ed5c9dd053194/crates/oxc_parser/src/js/expression.rs#L1228" target="_blank" rel="noopener noreferrer">oxc_parser/src/js/expression.rs</a>: 二項演算子の解析に使っています</p>
</li>
</ul>
<p>なので、再帰下降 ＋ Prattがわかればだいぶ良さそうです。</p>
<h2 id="pratt-parserについて覚えておきたいこと">Pratt Parserについて覚えておきたいこと</h2>
<p><a href="https://github.com/tsuka-ryu/compiler-workshop-rust/pull/6" target="_blank" rel="noopener noreferrer">rust版</a>を例に、コアのアイディアだけ自分が思い出せるように書いておこうと思います。コードとしては以下です。</p>
<p><code>a + b * c</code>と<code>a * b + c</code>をそれぞれちゃんとパースできるか、というのが今回見たい例です。（元のコードと違って、三項演算子に関係する部分は削除してます。）</p>
<pre><code class="language-rust">// 結合力の定義
fn infix_bp(&amp;self, tok: &amp;Token) -&gt; Option&lt;(u8, u8)&gt; {
    match tok {
        Token::Ternary =&gt; Some((2, 1)),
        Token::Plus =&gt; Some((3, 4)),
        Token::Multiply =&gt; Some((5, 6)),
        _ =&gt; None,
    }
}

// 二項演算子をパースして、式のASTを返す関数
fn parse_expr_bp(&amp;mut self, min_bp: u8) -&gt; Expression {
    // PART 1
    let mut lhs = self.parse_atom();

    // PART 2
    loop {
        // PART 3
        let (lbp, rbp) = match self.infix_bp(self.peek()) {
            Some(bp) =&gt; bp,
            None =&gt; break,
        };

        // PART 4
        if lbp &lt; min_bp {
            break;
        }

        // PART 5
        let op_tok = self.advance();

        let rhs = self.parse_expr_bp(rbp);
        let op = match op_tok {
            Token::Plus =&gt; BinaryOp::Add,
            Token::Multiply =&gt; BinaryOp::Multiply,
            other =&gt; unreachable!(&quot;infix_bp が Some を返したのに対応してない: {other:?}&quot;),
        };
        lhs = Expression::Binary {
            left: Box::new(lhs),
            op,
            right: Box::new(rhs),
        };
    }

    // PART 6
    lhs
}
</code></pre>
<p>ざっくり6つのPARTに分かれます。</p>
<ol>
<li><p>左辺（left-hand side）には<code>a</code>などのatomを代入します。2つ目で<code>break</code>になった場合、この値を最後の6つ目で返します。</p>
</li>
<li><p>loop部分、<code>break</code>しない場合、後続のPART 3〜5を繰り返します。</p>
</li>
<li><p><code>peek()</code>して次の演算子を読み、その結合力を受け取ります。もし、結合力を定義していないもの（<code>;</code>など）だったら<code>break</code>します。</p>
</li>
<li><p>prattの核の部分。左結合力と<code>min_bp</code>を比べて、<code>lbp &lt; min_bp</code>なら<code>break</code>、そうでなければ次の5つ目に進みます。<code>min_bp</code>は最初は<code>0</code>で、後述の5つ目で<code>+</code>のときであれば右結合力の<code>4</code>を、<code>*</code>であれば<code>6</code>を渡します。再帰呼び出しされた側では、見つけた演算子の左結合力と渡された<code>min_bp</code>の比較で、<code>break</code>するか次の手順に進むかが決まる！</p>
</li>
<li><p>演算子を消費し、右辺を<code>parse_expr_bp(rbp)</code>で再帰的にパースして、式のASTを<code>lhs</code>に代入する部分。</p>
</li>
<li><p><code>lhs</code>を返す</p>
</li>
</ol>
<p>なので、<code>+</code> vs <code>*</code> は、以下のような勝敗（？）になります。各オペランドの左右に隣の演算子の結合力を書いて、大きい方がそのオペランドを取り込む、と考えるとわかりやすいです。</p>
<p><code>a + b * c</code>の場合:</p>
<pre><code>a   +   b   *   c
  3   4
          5   6
</code></pre>
<p><code>b</code>の左右は<code>4</code>(=<code>+</code>のrbp)と<code>5</code>(=<code>*</code>のlbp)。<code>5 &gt; 4</code>で<code>*</code>の勝ち → <code>a + (b * c)</code></p>
<p><code>a * b + c</code>の場合:</p>
<pre><code>a   *   b   +   c
  5   6
          3   4
</code></pre>
<p><code>b</code>の左右は<code>6</code>(=<code>*</code>のrbp)と<code>3</code>(=<code>+</code>のlbp)。<code>6 &gt; 3</code>で<code>*</code>の勝ち → <code>(a * b) + c</code></p>
<p>すごいね！</p>
<h2 id="結合力の流儀">結合力の流儀</h2>
<h3 id="matkladの流儀">matkladの流儀</h3>
<p>matkladでは左右の結合力に差をつけて定義することで結合性を表現していました。<code>+</code>の<code>(3, 4)</code>のように右結合力を大きくすると、<code>a + b + c</code>は左結合で<code>(a + b) + c</code>になります（再帰側の<code>min_bp = 4</code>に対して次の<code>+</code>の左結合力<code>3</code>が負けて<code>break</code>するため）。逆に、三項演算子の<code>(2, 1)</code>のように左結合力を大きくすると右結合になります。一般化すると、</p>
<ul>
<li><p><code>lbp &lt; rbp</code> → 左結合（普通の算術演算子など）</p>
</li>
<li><p><code>lbp &gt; rbp</code> → 右結合（三項演算子、<code>**</code>、代入など）</p>
</li>
</ul>
<p>ただ、この記事を書きながらClaudeに教わったのですが、別の流儀もあるようです。</p>
<h3 id="oxcの流儀">oxcの流儀</h3>
<p>oxcでは、左結合か右結合かを事前に定義しています。</p>
<pre><code class="language-rust">impl Precedence {
    pub fn is_right_associative(self) -&gt; bool {
        matches!(self, Self::Exponentiation | Self::Conditional | Self::Assign)
    }

    pub fn is_left_associative(self) -&gt; bool {
        matches!(
            self,
            Self::Lowest
                | Self::Comma
                | Self::Spread
                | Self::Yield
                | Self::NullishCoalescing
                | Self::LogicalOr
                | Self::LogicalAnd
                | Self::BitwiseOr
                | Self::BitwiseXor
                | Self::BitwiseAnd
                | Self::Equals
                | Self::Compare
                | Self::Shift
                | Self::Add
                | Self::Multiply
                | Self::Prefix
                | Self::Postfix
                | Self::New
                | Self::Call
                | Self::Member
        )
    }
    // ...etc
}
</code></pre>
<p>また、優先度はペアではなく、一つの数字で表現されています。</p>
<pre><code class="language-rust">pub enum Precedence {
    Lowest = 0, // matkladでは初期値として 0 を直接代入しているが、oxcは最下位を enum の variant として明示することで型安全にしている
    Comma = 1,
    Spread = 2,
    Yield = 3,
    Assign = 4,
    Conditional = 5,
    NullishCoalescing = 6,
    LogicalOr = 7,
    LogicalAnd = 8,
    BitwiseOr = 9,
    BitwiseXor = 10,
    BitwiseAnd = 11,
    Equals = 12,
    Compare = 13,
    Shift = 14,
    Add = 15,
    Multiply = 16,
    Exponentiation = 17,
    Prefix = 18,
    Postfix = 19,
    New = 20,
    Call = 21,
    Member = 22,
}
</code></pre>
<p>上記の左右結合の定義と優先度を組み合わせることで、PART 4の<code>break</code>の箇所が以下のようになっています。</p>
<pre><code class="language-rust">let stop = if left_precedence.is_right_associative() {
    left_precedence &lt; min_precedence
} else {
    left_precedence &lt;= min_precedence
};

if stop {
    break;
}
</code></pre>
<p><a href="https://github.com/oxc-project/oxc/blob/d16f456ce93fcdadd2cf926afd1b68a6a84a31f8/crates/oxc_syntax/src/precedence.rs#L5-L15" target="_blank" rel="noopener noreferrer">oxcのコメント</a>にある通り、ECMAの仕様から導出した順番として定義したかったようです。</p>
<pre><code class="language-rust">/// Operator Precedence
/// 演算子の優先順位
///
/// The following values are meaningful relative position, not their individual values.
/// 以下の値は、それぞれの数値そのものではなく、相対的な位置関係に意味があります。
///
/// The relative positions are derived from the ECMA Spec by following the grammar bottom up, starting from the &quot;Comma Operator&quot;.
/// この相対的な位置関係は、ECMA仕様の文法を「カンマ演算子」を起点にボトムアップで辿ることで導かれています。
///
/// Note: This differs from the operator precedence table
/// &lt;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#table&gt;
/// but the relative positions are the same, as both are derived from the ECMA specification.
/// 注: これはMDNの演算子優先順位表とは異なりますが、どちらもECMA仕様から導かれているため、相対的な位置関係は同じです。
///
/// The values are the same as
/// [esbuild](https://github.com/evanw/esbuild/blob/78f89e41d5e8a7088f4820351c6305cc339f8820/internal/js_ast/js_ast.go#L28)
/// この値はesbuildと同じです。
</code></pre>
<p>いい話だなあ（？）</p>
<h2 id="まとめ">まとめ</h2>
<p>アルゴリズムとデータ構造に慣れ親しんでない自分からすると、Pratt Parserのようなコードはすぐ意味を見失ってしまうので、記事にしてみました。ついでにoxcの中もちょっとだけ覗けたので良かったですね。有意義な日曜日になりました。</p>
<p>Opus4.8が何でも知ってるので、コンパイラの勉強が捗ります。助かる。コンパイラ周りでまた記事にしたくなったら書こうと思います。それでは。</p>
<hr>
<p><em>最終更新: 2026年6月7日</em></p>
]]></content:encoded>
      <category>コンパイラ</category>
      <category>パーサー</category>
      <category>oxc</category>
    </item>
    <item>
      <title>GW振り返り</title>
      <link>https://tsuka-ryu.dev/posts/2026-05-09-end-of-gw</link>
      <guid isPermaLink="true">https://tsuka-ryu.dev/posts/2026-05-09-end-of-gw</guid>
      <pubDate>Sat, 09 May 2026 00:00:00 +0900</pubDate>
      <description>GWにやったことを振り返ります</description>
      <content:encoded><![CDATA[<p>ぼちぼちGWが終わろうとしてます。今年は予定もほとんどなく、かつ9連休という神環境だったので、引きこもってパソカタしたり、本や漫画読んだり、ゴロゴロお昼寝したり、ゼンタングルしたり、夜は毎日お酒を飲みに行ったりして過ごしていました。おうち大好き。</p>
<p>だいたい毎日虚無感に襲われつつ、自分が事前に計画したことをなるべく遂行しようと頑張ったので、GWにやったことを振り返ります。</p>
<h2 id="gwにやったこと">GWにやったこと</h2>
<p>思い出すためにざっくり書き出します。</p>
<ul>
<li><p>NeetCode - Algorithms and Data Structures for Beginners を通しでやる</p>
</li>
<li><p>Write a Compiler That Understands Types を見つつ実装する</p>
</li>
<li><p>Simple but Powerful Pratt Parsing を実装する</p>
</li>
<li><p>acorn の実装を眺める</p>
</li>
<li><p>Martin Grabmüller - Algorithm W Step by Step を読みつつ実装する</p>
</li>
</ul>
<h3 id="neetcode-algorithms-and-data-structures-for-beginners">NeetCode - Algorithms and Data Structures for Beginners</h3>
<p>やりました。Arrays、Linked Listsあたりから始まって、BSTやグラフの探索、DPなど一通り。もっと難しいトピックはAdvanced Algorithmsのほうに入ってるようですが、NeetCode（というかLeetCode？）のEasyやMediumを解くベースとしては十分な内容でした。</p>
<ul>
<li><p>動画を見る（優しい英語なので雰囲気で聞き取る）</p>
</li>
<li><p>問題を解く</p>
</li>
<li><p>半分くらいはわからん、写経する</p>
</li>
</ul>
<p>みたいなサイクル。動画の内容でつまづくことはなかったし、問題も癖がないので学んだことがすぐ活かせてよかったです。回答読むのめんどくさいなーってときも解説動画があって、ぼーっと眺めてれば良いのが助かります。</p>
<p>とはいえ、ちゃんと問題が解けるようになったわけではないので、今後訓練していきたい。復習して3日後くらいに解き直すとよいとClaudeが言っていました。個人的にやりたいのは、その場で解説を読むだけじゃなくて、「読む」→「その場ですぐ白紙に実装できるか再チャレンジ」という流れ。読むだけだと頭に残らない…</p>
<p>想定が25時間と書いてありますが、知ってる概念も多かったので10~15時間くらいでやれたんじゃないかと思います。測ってないけど。</p>
<h3 id="write-a-compiler-that-understands-types-を見つつ実装する">Write a Compiler That Understands Types を見つつ実装する</h3>
<p>この講座めっちゃよかった。これのためだけにFrontend Mastersに課金して、見終わったら退会しました。退会するの忘れそうなので。</p>
<p>概要は<a href="https://tsuka-ryu.dev/blog/2026-05-03-compilers">コンパイラ周りの勉強がしたくて</a>に書いたので割愛しますが、型DBを使ってUnification（単一化）することで、型推論や型チェックができる部分はだいぶ面白かったです。型推論という抽象的なものの実装イメージが湧きました。そういえば、WASMへのCode Generate部分はほぼ記憶ないな。</p>
<p>該当リポジトリには字句解析からCode Generationまで一通り揃ってるので、忘れた頃にまた眺めつつ、自分で実装できるか試したいと思っています。とても貴重な学びでした。</p>
<h3 id="simple-but-powerful-pratt-parsing-を実装する">Simple but Powerful Pratt Parsing を実装する</h3>
<p>かなり短いコードなのに、パーサーになってすげー！という感動がありました。めっちゃ前に眺めて何も覚えていなかった<a href="https://oxc-project.github.io/javascript-parser-in-rust/ja/" target="_blank" rel="noopener noreferrer">Write a JavaScript Parser in Rust</a>にも参考文献として書かれていて、繋がったなーと一人で驚いていました。今回それを理解できたのは素直に嬉しかったです。</p>
<p>メモった<a href="https://zenn.dev/sushin_ya/scraps/d789d17b98afcf" target="_blank" rel="noopener noreferrer">スクラップ</a></p>
<h3 id="acorn-の実装を眺める">acorn の実装を眺める</h3>
<p>眺めました。<a href="https://zenn.dev/sushin_ya/scraps/634eef6e351448" target="_blank" rel="noopener noreferrer">スクラップ</a>。</p>
<p>TokenizerとParser合わせても数千行なので、全然読めました。ただ読むのは退屈で、雑にClaudeに解説してもらって、へえーつって終わりました。</p>
<p><a href="https://github.com/mozilla-spidermonkey/jsparagus/blob/master/js-quirks.md" target="_blank" rel="noopener noreferrer">JS syntactic quirks</a>などに書かれているような曖昧な文法をどうやって対処してるんだろうと思ったのですが、再帰下降だと取り回しがしやすいのか、結構素直に書かれているのは驚きでした。もっと謎のテクニックを使ってるかと思ってましたが、案外読めた。例えば<code>/</code>が除算か正規表現か、<code>(</code>がアロー関数か引数リストか、などなど。</p>
<p>これもいざ自分で実装しろって言われたら全然できなさそうなので、小さいサブセットから始めてやってみたいなあと思っています。やるならTypeScriptのサブセットにしたい気持ちもありますが大変だってClaudeが言ってた。</p>
<h3 id="martin-grabmüller-algorithm-w-step-by-step-を読みつつ実装する">Martin Grabmüller - Algorithm W Step by Step を読みつつ実装する</h3>
<p>Milnerの提案したAlgorithm Wを実装することで、型推論について理解しようという論文です。論文というかほぼコード片が書いてあるだけで、なぜそうなってるかはClaudeに解説してもらわないと何もわかりませんでした。<a href="https://zenn.dev/sushin_ya/scraps/4b0caeebad7cfb" target="_blank" rel="noopener noreferrer">スクラップ</a>。</p>
<p>Write a Compiler That Understands Typesの型推論とほぼ同じようなことをしてるっぽいが、Haskellの抽象度が高いコードで再現されていてすごかったです。モナドトランスフォーマーって何だろうと思ってたので、具体例としてわかりやすかったです。</p>
<p>コードは抽象度が高すぎて雰囲気しかわからなかったのですが、直近で勉強していたことがつながった感じがして面白かったです。<a href="https://sdiehl.github.io/typechecker-zoo/introduction.html" target="_blank" rel="noopener noreferrer">Typechecker Zoo</a>にはRust実装もあるらしいので、そのうち読みたい。</p>
<h2 id="今後やりたいこと">今後やりたいこと</h2>
<ul>
<li><p><strong>TAPL を読む</strong>: ClaudeがひたすらTAPLを読めって言ってくるので、そろそろ読もうと思います</p>
</li>
<li><p><strong>関数型言語に再入門</strong>: 久々に入門し直したくなってきたので、前に物理本で買って売っちゃったラムダノートの『プログラミングHaskell 第2版』を電子版で買い直しました。ちょこちょこ読み進めていきたいです</p>
</li>
<li><p><strong>NeetCode150</strong>: ちまちまやっていきたい。Pythonは本当に楽で助かります。他の言語で勉強しようと思った時は意外と言語機能自体がノイズになって脳みそが回らない、みたいなことが多かったので、何も考えなくて適当に動く感じが気持ちいいです。コンパイラを書くにしても再帰とかバリバリ出てくるし、ビットマスクでの条件分岐の最適化とかacornの中でやってるし、そういうのシュッとわかるとかっこいい</p>
</li>
</ul>
<h2 id="まとめ">まとめ</h2>
<p>最近パソカタできてなかったので、まとまった時間でそれなりにいろいろできて楽しいGWでした。連休が明けても、パソカタ習慣を続けられるように頑張っていきたいです。最終日の明日は何しようかしら。</p>
<hr>
<p><em>最終更新: 2026年5月9日</em></p>
]]></content:encoded>
      <category>雑記</category>
      <category>コンパイラ</category>
      <category>型理論</category>
    </item>
    <item>
      <title>コンパイラ周りの勉強がしたくて</title>
      <link>https://tsuka-ryu.dev/posts/2026-05-03-compilers</link>
      <guid isPermaLink="true">https://tsuka-ryu.dev/posts/2026-05-03-compilers</guid>
      <pubDate>Sun, 03 May 2026 00:00:00 +0900</pubDate>
      <description>コンパイラ周りの勉強がしたくての話</description>
      <content:encoded><![CDATA[<h2 id="コンパイラってなんかカッコ良さそう">コンパイラってなんかカッコ良さそう</h2>
<p>コンパイラってなんかカッコ良さそうだな、というどうでもいいモチベで勉強したいなという気持ちです。何でですかね。やっぱJSエコシステム周辺にいると、TypeScriptやリンターフォーマッターの話題も多いので自然とそういうことに興味が湧いてくるのでしょうか。biomeやoxcなんかにコントリビュートできたらカッコ良さそうですよね。</p>
<p>自分が過去にした近い勉強としては、『コンピュータシステムの理論と実装』を読んだり（第１版で読んだ。そういえば3年前くらいのGWだったかな。）、Crafting Interpretersを読んだり、『型システムのしくみ』を読んだりとか。他にもコンパイラに関する本はたくさん買ってみたけど、まともに読み通せたのはそこらへん。</p>
<p>Crafting Interpretersはやっぱり決定版、という感じがあってtypescriptで<a href="https://github.com/tsuka-ryu/tlox" target="_blank" rel="noopener noreferrer">tlox</a>を実装したりもした。けど、あんまりバイトコードに興味がわかなくて第II部木の巡回によるインタプリタまでしか読んでない。GCの話題とかもあるらしいので読みたいんだけど、気が向いたらかな。JS界隈では結局、手書きの再帰下降パーサが多いみたいだし、本当に素晴らしい本です。また読み返したい。</p>
<h2 id="gwに向けてやっていたこと">GWに向けてやっていたこと</h2>
<p>GW時間がありそうなので、それまでにできそうなインプットはしようと思って、会社の行き帰りの電車でUdemyの動画を眺めていた。あと寝る前に『型システムのしくみ』を読み返したりした。</p>
<p>Udemyは<a href="https://www.udemy.com/user/dmitry-soshnikov/" target="_blank" rel="noopener noreferrer">Dmitry Soshnikov</a>という方の講座がよくて、何本か買ってみた。S式のtoy言語をパースしたり型チェックしたりトランスパイルしたりする。Crafting Interpretersや『型システムのしくみ』あたりをさらっと2時間くらいでやってくれるイメージ。</p>
<p>それ以外で買った講座は、理論を舐めるものが多く、手書きで証明とかする動画もあって、あんまりピンとこずに返金処理した。ちゃんと事前に中身を見た方が良いなと思った。</p>
<h2 id="gw中にやりたいこと">GW中にやりたいこと</h2>
<p><a href="https://frontendmasters.com/courses/type-compiler/" target="_blank" rel="noopener noreferrer">Write a Compiler That Understands Types</a>を見始めた。Richard FeldmanはZedの人で（そういえば最近v1がリリースされて盛り上がってるな）、Rocという言語も作ってる人らしい。お話がとっても上手で面白い。ElmやRustの講座もあって気になる。Frontend Mastersは初めて課金したけど、書き起こしがちゃんとあるのでGoogle翻訳しながら見れて助かる。講座の終わりの問題も地味に良い。この講座、自分が知りたかったところのど真ん中って感じの内容。大まかには以下のような流れ。サイトで目次も見れるし、<a href="https://github.com/rtfeldman/compiler-workshop-v1" target="_blank" rel="noopener noreferrer">コードやスライドはgithubに上がってる</a>ので助かる。これ見終わったら課金やめようと思ってたけど、それでも安心かな。</p>
<ul>
<li><p>Introduction: コンパイラの歴史</p>
</li>
<li><p>Traversing the AST: 構文木をトラバースして、フォーマットする演習があり楽しい</p>
</li>
<li><p>Bindings &amp; Scope Resolution: 名前解決、名前重複チェックなどの演習がある</p>
</li>
<li><p>Type Inference: 型推論、型のキーバリューストアをつくって推論できるようにするらしい。いまここ</p>
</li>
<li><p>Inferring Types: 型推論、関数とか</p>
</li>
<li><p>Polymorphism &amp; Type Signatures: 多相型、ジェネリクス、型注釈</p>
</li>
<li><p>Generating WASM: WebAssemblyにCode Generateする、これもいい</p>
</li>
</ul>
<p>ちゃんとトークナイザもパーサーもあるし、普通にリポジトリを読んでるだけでも勉強になりそう。今後も楽しみ。</p>
<p>あとちまちまNeetCodeをやってるので、木とか再帰関数とか出てきてなんか楽しい。コンパイラの良さ。</p>
<p>もうひとつは、<a href="https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html" target="_blank" rel="noopener noreferrer">Simple but Powerful Pratt Parsing</a>を読んで、自分で実装してみたいなという感じ。雑にトークナイザだけ書いたものがあるので、そこに肉付けしていきたい。</p>
<p>時間があったらCS143眺めたり、TAPLを真面目に読み始めたりしたい。acornやbabelのコードを読んでJSの大変さも知りたいし、とか色々やりたいことはあるけど多分時間が足りない。ぼちぼちやっていきたい。</p>
<h2 id="まとめ">まとめ</h2>
<p>やりたい、というお気持ち表明でしかないけど、GWはマジで何の予定もなくて助かる。ゆったりパソカタをして楽しくなっていきたい。業務でコードを書いてなさすぎるので、趣味でちゃんとコードを書いていきたい。</p>
<hr>
<p><em>最終更新: 2026年5月3日</em></p>
]]></content:encoded>
      <category>雑記</category>
      <category>コンパイラ</category>
    </item>
    <item>
      <title>2026年4月のよもやま</title>
      <link>https://tsuka-ryu.dev/posts/2026-04-30-review</link>
      <guid isPermaLink="true">https://tsuka-ryu.dev/posts/2026-04-30-review</guid>
      <pubDate>Sun, 19 Apr 2026 00:00:00 +0900</pubDate>
      <description>2026年4月のよもやまばなし</description>
      <content:encoded><![CDATA[<p>今月も技術ブログらしいものは書けなさそうなので更新。ついにただの雑記になった。</p>
<h2 id="axiosてんやわんや">axiosてんやわんや</h2>
<p>ことの顛末は<a href="https://www.aikido.dev/blog/axios-cve-2026-40175-a-critical-bug-thats-not-exploitable" target="_blank" rel="noopener noreferrer">Axios CVE-2026-40175: a critical bug that’s… not exploitable</a>に記載されているが、自分はこういう脆弱性についてもアンテナを張っている立場なので、最初重大度が10というとんでもないやつでびっくりした。</p>
<p>脆弱性の中身をみるとプロトタイプ汚染が前提だったり、Xで再現しないというツイートをしている人がいたりで、あれ？と思いつつ、けど重大度が10なので何もしないわけもなくという感じだった。社内の有志により確かに一部の古いバージョンのNodeを除いて再現しないことがわかり、自分も手元で試してまあそうだよね、という感じで一安心でした。</p>
<p>最近axiosは色々あって大変だけどOSSを利用させてもらってるだけなので、メンテナの方には頭が上がりませんな。axiosを新規で採用することはないが、メンテナの方が動いてくれてるうちはレガシーから頑張ってはがさなくてもいいかなと思ってしまう。というか剥がすコストが高すぎて大変。</p>
<h2 id="サプライチェーン攻撃対策を入れたよ">サプライチェーン攻撃対策を入れたよ</h2>
<p><a href="https://zenn.dev/dely_jp/articles/supply-chain-kowai" target="_blank" rel="noopener noreferrer">サプライチェーン攻撃から身を守るために最低限設定しておきたいこと</a>がすごく良くまとまっていて助かる記事。このブログにもminimumReleaseAgeとかcooldownとか、諸々設定しました。Claudeくんにお願いしたらシュッと設定してくれたので超楽ちん。</p>
<p>去年、Nxのnpmパッケージがサプライチェーン攻撃を受けた件があったときにJxckさんが<a href="https://blog.jxck.io/entries/2025-09-20/mitigate-risk-of-oss-dependencies.html" target="_blank" rel="noopener noreferrer">サプライチェーン攻撃への防御策</a>を書いてくれていたけど、今年は本格的に導入しておかないとまずいぞ、という雰囲気が漂っている。実害が出ているところもありそう。OSSの発展のことを考えるとやめていただきたいですな。</p>
<h2 id="wcag2-2解説書を読んだ">WCAG2.2解説書を読んだ</h2>
<p>社内の有識者に解説してもらったのを元に、解説書を一通り読んだ。まだコンポーネント実装やページ実装のプラクティスと有機的に結びついてないので、そこら辺をこれからやっていきたい。自分の理解が追いついていないせいかもしれないけど、世のアクセシビリティ関連の記事や本、WCAGの達成基準とユーザビリティをごちゃ混ぜにして書いてるから逆にわかりにくくなっている気がしてきた。まだスタートラインに立っただけの感想ですが…</p>
<p>『Webアプリケーションアクセシビリティ』やAPGを読み返していきたい、他の企業さんのアクセシビリティガイドラインを読んでいきたい。そんな気持ち。</p>
<h2 id="最近買ったものリスト">最近買ったものリスト</h2>
<p>半年間くらい私生活がバタバタ（？）していたのが終わったので、しばらく暇で、仕事も穏やかそうなので心機一転色々勉強していきたい。ついでにこのブログにも書いていきたい。</p>
<p>技術関連の本を色々買った。半分は物理で読んだのを買い直した形。型システム入門は思ったよりとっつきやすそうで、そのうちちゃんと読みたいと思いながらパラパラ読んでる。Leanは言語として面白そう。けど数学の概念何もわからん。</p>
<ul>
<li><p>ゼロから始めるLean言語入門</p>
</li>
<li><p>型システムのしくみ</p>
</li>
<li><p>型システム入門</p>
</li>
<li><p>ユースケース駆動開発実践ガイド</p>
</li>
<li><p>関数型ドメインモデリング</p>
</li>
</ul>
<p>動画講座を色々買った。<a href="https://yuki0920.hatenablog.jp/entry/20260418/1776510000" target="_blank" rel="noopener noreferrer">何度も挫折した自分が、LeetCode150問でデータ構造とアルゴリズムをやっと理解できた話</a>を読んで、確かに本だと挫折し続けてきたから動画にしようかな、と思った。そのことについてちゃっぴーとはなしていたら、コンパイラ周りも動画講座をおすすめされて、ちょうどセールだったのでどかっと買った。買いすぎたかも。けどGWも予定ないし、引きこもってパソカタしたいな。</p>
<ul>
<li><p>NeetCodeのlife time、動画講座見るため</p>
</li>
<li><p><a href="https://www.udemy.com/course/essentials-of-interpretation/" target="_blank" rel="noopener noreferrer">Building an Interpreter from scratch</a></p>
</li>
<li><p><a href="https://www.udemy.com/course/compiler-design-course/" target="_blank" rel="noopener noreferrer">The Complete Course of Compiler Design</a></p>
</li>
<li><p><a href="https://www.udemy.com/course/mastering-compiler-design-basics-to-advanced-techniques/" target="_blank" rel="noopener noreferrer">Mastering Compiler Design: Basics to Advanced Techniques</a></p>
</li>
<li><p><a href="https://www.udemy.com/course/programming-language-with-llvm/" target="_blank" rel="noopener noreferrer">Programming Language with LLVM</a></p>
</li>
</ul>
<h2 id="まとめ">まとめ</h2>
<p>今月は脆弱性周りでばたついてた気がする。しばらく何もないことを祈る。</p>
<p>それはそうと、仕事でほぼパソカタしないので、土日とGWでまとめてパソカタしていきたい。勉強したことはブログに書きたい。あと連休はClaude App触ったり、最近のClaude Codeどんな感じかも調べたいな。</p>
<p>仕事で実装タスクがないとどんどんおいていかれている感じがする。別にいいんだけど。AI周りは正直食傷気味なので、自分を強くするムーブをしていきたいですな。</p>
<hr>
<p><em>最終更新: 2026年4月19日</em></p>
]]></content:encoded>
      <category>雑記</category>
    </item>
    <item>
      <title>2026年3月の技術メモ</title>
      <link>https://tsuka-ryu.dev/posts/2026-03-29-review</link>
      <guid isPermaLink="true">https://tsuka-ryu.dev/posts/2026-03-29-review</guid>
      <pubDate>Sun, 29 Mar 2026 00:00:00 +0900</pubDate>
      <description>2026年3月に気になった技術的なトピックのメモ</description>
      <content:encoded><![CDATA[<p>毎月1本は技術記事を書こうと思っていたのですが、今月は何も書いてなかったので苦し紛れに更新します。業務でコードをほぼ書くことがない（泣）のですが、日々記事を読んだり本を読んだりはしてるので、そこら辺の断面を記録する意図でもあります。</p>
<h2 id="async-react">Async React</h2>
<p>Async Reactの話題をちらほら見ることが多かった。<a href="https://zenn.dev/uhyo/articles/async-react-debounce" target="_blank" rel="noopener noreferrer">uhyoさんによる記事</a>の影響なのかな。</p>
<p><code>await UI = await f(await state)</code>の世界観で、より宣言的に考えるという感じっぽい。</p>
<p>なるほどと思いつつ、コンポーネントライブラリやルーター、データフェッチングライブラリなども含めての統合、ということでいまいちピンとくるようなこないような。いままでNext.jsなんかのフレームワークが解決していたところとの境界どうなるんだろう、という気持ちもありつつ、宣言的なのは良いことなので楽しみですな。</p>
<p>業務で複雑なフォーム周りの実装を見ていると、なんだかんだReact-hook-formを使って複雑さと型の部分で苦しんでいたりするので、そこらへんもReact本体でいい感じにしてくれないかな、と思ったりしないこともないです。</p>
<p><a href="https://zenn.dev/sushin_ya/scraps/d2dab2973eb25a" target="_blank" rel="noopener noreferrer">自分が雑に眺めたスクラップ</a></p>
<h2 id="typescript-6-0-7-0">TypeScript 6.0 / 7.0</h2>
<p><a href="https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/" target="_blank" rel="noopener noreferrer">Announcing TypeScript 6.0</a>の通り、6.0がリリースされました。7.0もあと数ヶ月で出るらしく、そこに向けたオプションの変更や、並行処理するためのお手当などが含まれてる感じですね。あとはes2025周りの取り込み。</p>
<p>悲観的なことを言うと、OxcなんかのRust実装のおかげで、リンター・フォーマッターはめちゃくちゃ高速化されたわけですが、TSがGoによる再実装で10倍速くなるとはいえ、そこがボトルネックになるのは変わらないのかなという気持ち。この変更で何がどれくらい変わるんですかね。その再実装のおかげで型推論周りですごい新機能が実装しやすい、とかあったりするんでしょうか。TSは機能的には十分な気もするのでまあ良いか。</p>
<h2 id="astro-solid-vite-next-js">Astro / Solid / Vite / Next.js</h2>
<p>Astroは<a href="https://astro.build/blog/astro-6/" target="_blank" rel="noopener noreferrer">6.0</a> / <a href="https://astro.build/blog/astro-610/" target="_blank" rel="noopener noreferrer">6.1</a>がリリースされ、Solidは<a href="https://github.com/solidjs/solid/releases/tag/v2.0.0-beta.0" target="_blank" rel="noopener noreferrer">2.0beta</a>がリリースされ、<a href="https://vite.dev/blog/announcing-vite8" target="_blank" rel="noopener noreferrer">Viteは8でRolldownが正式統合され</a>、Next.jsは<a href="https://nextjs.org/blog/next-16-2-ai" target="_blank" rel="noopener noreferrer">16.2</a>がリリースされ、と言う感じ。</p>
<p>自分の周りでは、Next.jsは否定的な意見がどんどん増えていて、新規プロダクトはAstro+Reactの組み合わせが多い。Tanstack Startなんかを取り入れている案件もあるくらい。微妙に迷走していたAstroが<a href="https://blog.cloudflare.com/ja-jp/astro-joins-cloudflare/" target="_blank" rel="noopener noreferrer">cloudflareに買われた</a>のはよかった気がする。MPAフレームワークはメディアサイトなどで需要があるし、Next.jsでRSCをゴリゴリに使うにはちょっと腰が重いときに選択肢にしてもすぐにはなくならないな、と言う気持ちになった。Netlifyに買われたGatsbyやShopifyに買われたRemixのことが少し頭をよぎった。</p>
<p>Solidは筋は良さそう（？）なのに流行らないし、相変わらずReact一強感は揺るがない気がしているけど、SolidがAPIを結構変えて新たに思想出してきてるのはいい話な気がする。まあ個人的にはSignalがそんなにいいのか？というのはちょっと疑問だけど、確かにNuxt / Vueの実装を眺めることがあって読んでるとproxyで連動する様は美しいなあと思ったりはした。Nuxtは自分が触った範囲ではメモリ周りで気になることがあったけど、そこはSignalとは関係ないのかな。</p>
<p>Next.jsは目立ったリリースはなくて、AI周りとの統合っていう方向性が多い気がする。PPRやDynamic IO的なものはみんなが求めている方向じゃないんだろうなと言う気がする。leerob氏もCursorに行ってしまった影響も個人的には大きい気がしなくもない。与太話程度に。</p>
<p>後述する<a href="https://nextjs.org/blog/nextjs-across-platforms" target="_blank" rel="noopener noreferrer">adapterの話</a>はいい話だと思うけど、業務ではECSを使ったAWSの構成がほとんどであんまり影響がない。middlewareを復活してもっと自由にしてくれよ、とかエラーハンドリング周りの自由度を上げてくれよ、とかの気持ちがなくはない。コミュニティ的には素晴らしい話だと思う。</p>
<p>そういえば<a href="https://vinext.io/" target="_blank" rel="noopener noreferrer">Vinext</a>なんてありましたね。</p>
<h2 id="vite-void-adapters-opennext">Vite+ / Void / Adapters / OpenNext</h2>
<p>Vite+がOSSとしてリリースされた。Void Zero立ち上げ当初、Vite+でマネタイズするという話をチラッと見かけて無理では？と思ってたけどやっぱりそうね。とはいえOxc周りの登場により、急速にフロントエンドツールチェーンが大統一された感じがあり、すごいね。正直vpコマンドを使うかはわからないけど。npmxやe18eなんかの活動もVoid Zero周りの人がやってるのかな？新風吹きまくってて、眺めているだけでも小気味よい。</p>
<p>一方で<a href="https://void.cloud/" target="_blank" rel="noopener noreferrer">(V)ite (O)ptimized (I)somorphic (D)eploy</a>が発表されてそっちかー、となった。Cloudflareに密結合する形で構成されたホスティングサービス？らしい。Deno deployとかそっちの方向と同じ感じなのかな。けどこれも成功するんだろうか。<a href="https://2025.stateofjs.com/ja-JP/other-tools/#hosting" target="_blank" rel="noopener noreferrer">State of JS 2025のホスティング</a>を眺めていると、Vercelはそれなりに成功してそうって感じだったけど、AWSがやっぱり強いのかなという気持ちでVoidを業務で使うことはないだろうなと遠目に眺めている。今見返すと、NetlifyやCloudflareもシェア結構あるな。</p>
<p>近い話で大きかったのが<a href="https://nextjs.org/blog/nextjs-across-platforms" target="_blank" rel="noopener noreferrer">Next.js Across Platforms: Adapters, OpenNext, and Our Commitments</a>だ。もともとAWSでISRしたりするのむずくてVercel一択だったのに対して、NetlifyやOpenNextなんかが頑張った結果が出た感じ。Next.jsはVercelにベンダーロックインされすぎていて批判を浴びてたのもあって動いたって感じかな。RSCになってキャッシュがより複雑になったけどそこらへんにも対応してるんだろうか？何も知らないな。</p>
<p>Cloudflareなんかでいい感じに動くのであればそれはそれで嬉しそう、Amplifyはうーんあんまり使いたくないけど手軽になるなら嬉しそうって感じ。結局Next.jsが今後使われ続けるのかどうかもわからなくなってきてるので、諸々、今後次第かな。OSSという文化の視点では良いことに思えます、たぶん。</p>
<h2 id="figma-mcp">Figma MCP</h2>
<p>Figma MCPがちょこちょこ使われ始めているのを感じる。それに<code>use_figma</code>が生えたのが大きくて、Figma Makeを経由しなくても、デザインとコードを行き来しやすくなった。参考記事は<a href="https://azukiazusa.dev/blog/use-figma-mcp-tool/" target="_blank" rel="noopener noreferrer">azukiazusa氏のブログ</a>あたり。自分は本当に少しだけ触ってみたけど、設定何もしなくてもMCPさえあれば動いてよかった。</p>
<p>個人的にはコードファーストでデザイン不要だ！みたいなことを言ってるのはプロトタイプの世界で、あくまでデザイナーさんの道具はFigmaだろうと思ってるので、結局それは使われる気がしている。ただ、AI周りのツールによってデザイナーさんが楽になるのであれば良いこと。</p>
<p>一方で、Figmaファイルの構造やデザインシステムの重要度が高まっている気がして、MCPを使うにしても整理されてないファイルだと効果は限定的に思える。デザイナーさんとのコミュニケーションをやっていかないといけないなあという気持ち。デザインの質がコーディングの生産性に影響するというのはちょっと難しい問題だ。</p>
<h2 id="デザイナーのためのコンポーネント設計論">デザイナーのためのコンポーネント設計論</h2>
<p><a href="https://zenn.dev/yy/books/b62994c6b0b1e9" target="_blank" rel="noopener noreferrer">デザイナーのためのコンポーネント設計論</a>がめっちゃよかったです。これ無料で読めるの助かる。エンジニアでも陥りがちな間違いや設計方針についてわかりやすく解説されていて助かりますね。今後も繰り返し読みたいと思いました。</p>
<p>デザインファイル自体の重要度も上がっていくこともあり、デザインを作成するフェーズにもっとエンジニアがコミットしていかないといけないと言う気持ちになる。かつアクセシビリティ観点で考えると、デザインや仕様の時点で気をつけたいことが多い。いままでは実装で誤魔化していたような微妙なデザインや仕様がアクセシビリティ観点で問題になることをちょこちょこ見かけるようになりつつある。</p>
<h2 id="アクセシビリティ周り">アクセシビリティ周り</h2>
<p>アクセシビリティ周りは今年一番やりたいことだったのでちまちま進めている。社内の有識者に<a href="https://waic.jp/translations/WCAG22/Understanding/" target="_blank" rel="noopener noreferrer">WCAG2.2の解説書</a>を解説してもらったり、関連記事を読んだりしていてとても勉強になっている。自分も実際にアクセシビリティチェックができるようになっていきたい気持ち。WCAGに準拠するための実装方針やレビュー観点なんかも社内でナレッジを貯めていきたい。</p>
<p>masup9さんが<a href="https://masup9.github.io/apg-patterns-examples/ja/" target="_blank" rel="noopener noreferrer">APG Pattern Example</a>を実装されていたり、<a href="https://github.com/masuP9/a11y-specialist-skills" target="_blank" rel="noopener noreferrer">a11y-specialist-skills</a>を公開されていたりするので大変助かる。ここら辺も参考にしつつ社内でやらないといけないことを整理していきたい。</p>
<h2 id="ハーネスエンジニアリングとモデルの進歩">ハーネスエンジニアリングとモデルの進歩</h2>
<p>最近業務で全然コードを書いていないし、趣味でも何もやる気が起きず映画ばっかり見ているのでAI周りのキャッチアップができていない。<a href="https://zenn.dev/shio_shoppaize/articles/shogun-harness-engineering" target="_blank" rel="noopener noreferrer">ハーネスエンジニアリング</a>がどうの、と言う記事もちらほら見てますけど、hookやpre-commit的な部分でガードしていくのはいいと思いますけど、概念自体はそんなに目新しくないかなという印象で、ちょっと斜に構えて眺めています。</p>
<p>LLMに対するスタンスは<a href="https://podcasts.apple.com/jp/podcast/422-abstracting-k-pop-naoya/id603013428?i=1000757168896" target="_blank" rel="noopener noreferrer">naoyaさんがRebuildで言っていた通り</a>、変に最適化するような工夫をしても半年もすればLLM開発側が吸収してプロダクトに組み込んでしまうし、モデルも乗り換えのハードルがめちゃくちゃ低いのでそんなに焦ってキャッチアップしなくても良いかなと言う気がしています。</p>
<p>唯一今までと違うなーというのは並行開発的なことができる部分な気もするのですが、脳の負荷が高そうなので結局あんまり試せてないですね。業務でやれる機会があったら頑張りたいなあくらいの気持ちで構えています。</p>
<p>OpusもCodexも賢いし、Geminiもそこそこいいし、LLMバブルはどうなっていくんでしょうね。使えなくなるとそれはそれで困るけどって気持ち。CursorのComposer 2.0はKimiベースか何からしいので、そのうちOSSでまともなモデルが出てきて全部無料になったりしないかな。</p>
<p>あとAGI的なものはしばらく出てこなさそうだな、という気持ちも芽生えつつありますが、どうなんでしょうね。現状でも人間よりも賢い気はするのですが、いまのところ知性のようなものは感じない気がしなくもないです。むしろ人間が知性だと思っていたことの大半はただの言語ゲームで、LLMはそのゲームが上手い、というのが真相な気がします。アインシュタインやフォン・ノイマンのような感じにAIはなれるんでしょうか。</p>
<h2 id="まとめ">まとめ</h2>
<p>フロントエンドエコシステム周りも、LLMも、Figmaやその周辺も目まぐるしく進歩はしていて、眺めていて飽きないですね。といっても本当に眺めているだけなので、それはどうなんだという気持ちはしつつ、何もできていないですね。虚無。ブログの盆栽もできてないし、来月は何かしたいです。それではまた。</p>
<hr>
<p><em>最終更新: 2026年3月29日</em></p>
]]></content:encoded>
      <category>振り返り</category>
      <category>雑記</category>
    </item>
    <item>
      <title>タグ機能を追加</title>
      <link>https://tsuka-ryu.dev/posts/2026-02-11-tags-feature</link>
      <guid isPermaLink="true">https://tsuka-ryu.dev/posts/2026-02-11-tags-feature</guid>
      <pubDate>Wed, 11 Feb 2026 00:00:00 +0900</pubDate>
      <description>タグ機能を追加したのでメモ</description>
      <content:encoded><![CDATA[<h2 id="概要">概要</h2>
<p>だいたいのブログについてるタグ機能を実装しました。Claude Codeに書いてもらいました。</p>
<p>ちなみに<a href="https://sijiaoh.com/" target="_blank" rel="noopener noreferrer">怠惰駆動開発</a>さんがシンプルながらカッコいいブログなので真似させていただきました。</p>
<h2 id="実装要件">実装要件</h2>
<p>以下のような要件で実装しました。</p>
<ul>
<li><p><code>/tags</code>でタグ一覧ページ</p>
</li>
<li><p><code>/tags/[tag]</code>で各タグのページ（Static Site Generationで生成）</p>
<ul>
<li><p><code>/blog?tag=hoge</code>という選択肢もありましたが、Dynamicになるため見送りました</p>
</li>
</ul>
</li>
<li><p>記事一覧とタグページを相互にリンク</p>
</li>
</ul>
<h2 id="実装中の課題と気づき">実装中の課題と気づき</h2>
<h3 id="リンク一覧とアクセシビリティ">リンク一覧とアクセシビリティ</h3>
<p><code>/blog</code>でタグのリンクを追加すると、キーボード操作でリンクを移動する時にたくさん移動することになって大変だなと思いました。</p>
<p><a href="https://nextjs.org/blog" target="_blank" rel="noopener noreferrer">Next.js blog</a>でも同様の実装ですが、最適な方法は何か気になりました。GitHubのissueページを見ると、listitemで矢印移動が使いやすく実装されていて、なるほどと思いました。</p>
<p><a href="https://azukiazusa.dev/blog/link-area-delegation-proposal/" target="_blank" rel="noopener noreferrer">カード UI の入れ子のリンクの問題を解決する Link Area Delegation の提案</a>あたりをちゃんと読めばいいんですが、休日のバイブコーディングなので、また今度。</p>
<p>アクセシビリティを意識しようとするとナニモワカラナイになるのをやめたい。</p>
<h3 id="slug問題と日本語対応">slug問題と日本語対応</h3>
<p>適当に日本語タグで実装してから気がついたのですが、Claude Codeは<code>encodeURIComponent</code>を使って雑にslugにしてました。流石に微妙かなと思ったので、<code>slugify</code>を入れたのですが、日本語対応してなかったので<code>transliteration</code>を入れてみました。</p>
<p>実装してみたら妙に精度いいな、と思ったら勝手にClaude Codeがカスタム辞書を定義してて、騙されるところでした。危ない。</p>
<p>正直<code>transliteration</code>の結果は結構微妙なので、ここは形態素解析していい感じに変換するか、LLM使うかの自前実装したいなと思いました。</p>
<h3 id="claude-codeのコード品質">Claude Codeのコード品質</h3>
<p>やたら<code>as</code>を使ったり、手続きっぽい書き方だったりして微妙なコードでした。Skillsを導入した方がいいかな。気が向いたらやろう。</p>
<h2 id="その他の改善">その他の改善</h2>
<h3 id="開発環境の整備">開発環境の整備</h3>
<p><code>lefthook</code>で<code>pre-push</code>時に<code>format</code>と<code>build</code>を実行することにしてみました。いまはvercelにデプロイしててCIをまともに回してないので代わりです。</p>
<h3 id="コードのリファクタリング">コードのリファクタリング</h3>
<p>重複するコードを共通化したり細かいリファクタもしました。</p>
<p>そろそろ自動テストが欲しくなってきた気がする。</p>
<h2 id="まとめ">まとめ</h2>
<p>タグ機能の実装を通じて、以下のような気づきがありました。</p>
<ul>
<li><p><strong>アクセシビリティ</strong>: リンク一覧のキーボード操作など、UX面での課題</p>
</li>
<li><p><strong>国際化対応</strong>: 日本語タグのslug生成の難しさ</p>
</li>
<li><p><strong>コード品質</strong>: AI生成コードのレビューの重要性</p>
</li>
<li><p><strong>開発体験</strong>: lefthookによるローカルCI/CDの整備</p>
</li>
</ul>
<p>タグ機能ひとつとっても、意外と考えることが多いですね。気軽に1時間くらいで実装しようと思ったのですが、なんだかんだ2時間半くらいかかりました。</p>
<p>タグ機能ができたので、雑にエッセイ記事を書いてもいい気がしてきました。</p>
<p>それではまた。</p>
<hr>
<p><em>最終更新: 2026年2月11日</em></p>
]]></content:encoded>
      <category>ブログ盆栽</category>
      <category>タグ</category>
    </item>
    <item>
      <title>RSSフィードについて</title>
      <link>https://tsuka-ryu.dev/posts/2026-02-02-rss-feature</link>
      <guid isPermaLink="true">https://tsuka-ryu.dev/posts/2026-02-02-rss-feature</guid>
      <pubDate>Mon, 02 Feb 2026 00:00:00 +0900</pubDate>
      <description>RSSフィードを改善したのでメモ</description>
      <content:encoded><![CDATA[<p>RSSフィードの実装を改善したのでメモります。</p>
<h2 id="発端">発端</h2>
<p>自分は普段inoreaderを使って情報収集をしています。そのため、このブログを作ったときも自然とRSSフィードの機能をつけてニヤニヤしていました。</p>
<p>ただ、いくつかの記事を見るうちに、見出しがうまく表示されていないことに気がつきました。原因の詳細は不明なのですが、おそらくHTMLの構造が複雑なので、inoreaderのパーサーが複雑なHTML構造を適切に処理できていないことがわかりました。</p>
<p>以下のようにヘッダータグ内に<code>svg</code>が含まれる複雑な構造になっています。</p>
<pre><code class="language-html">&lt;h2 class=&quot;flex scroll-m-28 flex-row items-center gap-2&quot; id=&quot;tldr&quot;&gt;
  &lt;a data-card=&quot;&quot; href=&quot;#tldr&quot; class=&quot;peer&quot;&gt;tl;dr&lt;/a
  &gt;&lt;svg
    xmlns=&quot;http://www.w3.org/2000/svg&quot;
    width=&quot;24&quot;
    height=&quot;24&quot;
    viewBox=&quot;0 0 24 24&quot;
    fill=&quot;none&quot;
    stroke=&quot;currentColor&quot;
    stroke-width=&quot;2&quot;
    stroke-linecap=&quot;round&quot;
    stroke-linejoin=&quot;round&quot;
    class=&quot;lucide lucide-link size-3.5 shrink-0 text-fd-muted-foreground opacity-0 transition-opacity peer-hover:opacity-100&quot;
    aria-hidden=&quot;true&quot;
  &gt;
    &lt;path d=&quot;M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71&quot;&gt;&lt;/path&gt;
    &lt;path d=&quot;M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71&quot;&gt;&lt;/path&gt;
  &lt;/svg&gt;
&lt;/h2&gt;
</code></pre>
<h2 id="修正その①">修正その①</h2>
<p>Claudeと相談した結果、RSS用にクエリパラメータを付与すると良いということなので、その通りに修正しました。<a href="https://github.com/tsuka-ryu/tsuka-ryu-s-blog/pull/3" target="_blank" rel="noopener noreferrer">実際の修正PR</a>。</p>
<pre><code class="language-jsx">// 主要な部分を抜粋
const searchParams = await props.searchParams;

const isRSSView = searchParams?.view === &quot;rss&quot;;

const components = isRSSView
  ? {
      h1: &quot;h1&quot;,
      h2: &quot;h2&quot;,
      h3: &quot;h3&quot;,
      h4: &quot;h4&quot;,
      h5: &quot;h5&quot;,
      h6: &quot;h6&quot;,
    }
  : undefined;

// クエリパラメータがある場合は、`getMDXComponents`のヘッダータグを上書き
&lt;Mdx components={getMDXComponents(components)} /&gt;;
</code></pre>
<h2 id="修正その②">修正その②</h2>
<p>その後にふとした拍子に<code>/blog/[slug]</code>がSSGされていないことに気がつきました。<code>searchParams</code>を使ってるから当然なのですが、前の修正時には気づかなかったですね。</p>
<pre><code class="language-bash">Route (app)
┌ ○ /
├ ○ /_not-found
├ ○ /about
├ ƒ /api/search
├ ○ /blog
├ ƒ /blog/[slug]
├ ○ /blog/rss.xml
├ ● /docs/[[...slug]]
│ ├ /docs
│ └ /docs/test
├ ○ /icon.png
└ ● /og/blog/[...slug]
  ├ /og/blog/2025-12-30-my-blog/image.webp
  ├ /og/blog/2025-12-31-recap/image.webp
  ├ /og/blog/2026-01-11-og-image/image.webp
  └ [+2 more paths]


○  (Static)   prerendered as static content
●  (SSG)      prerendered as static HTML (uses generateStaticParams)
ƒ  (Dynamic)  server-rendered on demand
</code></pre>
<p>なので、クエリパラメータをやめて、<code>/blog/[slug]/rss</code>というパスパラメータにすることにしました。</p>
<p>ビルド時にちゃんとSSGされるようになったので問題ないと思います。</p>
<pre><code class="language-bash">Route (app)
┌ ○ /
├ ○ /_not-found
├ ○ /about
├ ƒ /api/search
├ ○ /blog
├ ● /blog/[slug]
│ ├ /blog/2025-12-30-my-blog
│ ├ /blog/2025-12-31-recap
│ ├ /blog/2026-01-11-og-image
│ └ [+2 more paths]
├ ● /blog/[slug]/rss
│ ├ /blog/2025-12-30-my-blog/rss
│ ├ /blog/2025-12-31-recap/rss
│ ├ /blog/2026-01-11-og-image/rss
│ └ [+2 more paths]
├ ○ /blog/rss.xml
├ ● /docs/[[...slug]]
│ ├ /docs
│ └ /docs/test
├ ○ /icon.png
└ ● /og/blog/[...slug]
  ├ /og/blog/2025-12-30-my-blog/image.webp
  ├ /og/blog/2025-12-31-recap/image.webp
  ├ /og/blog/2026-01-11-og-image/image.webp
  └ [+2 more paths]


○  (Static)   prerendered as static content
●  (SSG)      prerendered as static HTML (uses generateStaticParams)
ƒ  (Dynamic)  server-rendered on demand
</code></pre>
<h2 id="まとめ">まとめ</h2>
<p>RSSフィードの対応をしました。</p>
<p>App routerをちゃんと使ったことないので、こういう勘所がないということに気がつきました。</p>
<p>意図しない変更を避けるために、ビルド結果のsnapshotの差分を見たほうがいいかも？とも思いました。こういう気づきがあるので個人開発はいいですね。</p>
<hr>
<p><em>最終更新: 2026年2月2日</em></p>
]]></content:encoded>
      <category>ブログ盆栽</category>
      <category>RSSフィード</category>
    </item>
    <item>
      <title>開発環境現状確認2026</title>
      <link>https://tsuka-ryu.dev/posts/2026-01-22-dev-env</link>
      <guid isPermaLink="true">https://tsuka-ryu.dev/posts/2026-01-22-dev-env</guid>
      <pubDate>Thu, 22 Jan 2026 00:00:00 +0900</pubDate>
      <description>開発環境現状確認2026をメモリます。</description>
      <content:encoded><![CDATA[<p>今更だけど流行っていたので便乗してみます。</p>
<h2 id="tl-dr">tl;dr</h2>
<ul>
<li><p>エディタ: VSCode + Claude Code / Copilot / Cursor</p>
</li>
<li><p>ターミナル: ghostty + zsh + Oh My ZSH!</p>
</li>
<li><p>言語: TypeScript メイン、Python 少々</p>
</li>
<li><p>ブラウザ: Arc</p>
</li>
<li><p>物理: M3 Air / HHKB雪 / Keychron Q1</p>
</li>
</ul>
<h2 id="エディタ-ide">エディタ/IDE</h2>
<p>個人用はVSCode+Claude CodeのVSCode拡張。</p>
<p>仕事ではVSCode+Copilot Chat（基本Claude Sonnet 4.5、たまにOpus）か、Cursorのどちらかを使ってます。コードを書く仕事はあんまりなくて、コードを読むために使ってる。</p>
<p>全部Vim拡張を入れていて、雰囲気でそれっぽいキーバインドを使ってます。</p>
<h2 id="ターミナル">ターミナル</h2>
<p>Hyperを使っていたけどTahoeにしてから起動がもっさりするようになったので、<a href="https://ghostty.org/" target="_blank" rel="noopener noreferrer">ghostty</a>に乗り換えました。いまのところサクサクで楽しい。設定は<a href="https://zenn.dev/link/comments/f81bb7d1555096" target="_blank" rel="noopener noreferrer">スクラップ</a>に書きました。</p>
<p>シェルは<code>zsh</code>で、雑に<code>Oh My ZSH!</code>を入れている。
テーマは<code>powerlevel10k/powerlevel10k</code>。</p>
<p>zshプラグインは<code>(git fzf-tab zsh-syntax-highlighting zsh-bat mise)</code>らしい。<code>zsh-bat</code>なんて入れてたっけ。</p>
<p><a href="https://github.com/x-motemen/ghq" target="_blank" rel="noopener noreferrer">ghq</a>は必須でかなり使う、fzfと組み合わせで使ってます。</p>
<p>dotfilesはやりたいと思いつつ、PC買い直したときはまっさらから何入れよっかなーを考えるのが好きなので使ってないです。<a href="https://www.chezmoi.io/" target="_blank" rel="noopener noreferrer">chezmoi</a>がいいんだっけ？</p>
<p>Figの補完に頼り切ってたのでAmazon Qになって使えなくなったのがマジでつらかった。ただ、最近VSCodeのデフォルトターミナルに補完が来たのでなんとかなるようになりました。</p>
<h2 id="aiツール">AIツール</h2>
<p>個人で課金してるのはClaude Codeのみ。毎月$20を献金してます。それ以外は課金してない。</p>
<p>CopilotもCursorも使ってるけど、Claude Codeが一番馴染むかも。</p>
<h2 id="ブラウザ">ブラウザ</h2>
<p>メインブラウザはいまだにArc。</p>
<p>Chromeに戻ろうとしたり、Firefoxにしてみたり、Vivaldi触ったりしたけどいまだにArcから離れられない。</p>
<p>Chromeに垂直タブも画面分割もきたけど、スペース機能が欲しいな。あと<code>Cmd+T</code>でランチャーっぽくURL打ったり履歴探したりするのが地味に嬉しいんだけど、これもChromeに入らないかな。</p>
<h2 id="プログラミング言語-ランタイム">プログラミング言語・ランタイム</h2>
<p>TypeScript。最近競プロの勉強をしたくてちょこちょこPython。RustやHaskellやりたいけどなあ。あとLeanとか気になりはする。けどやっぱやりたいのはHaskellなんだろうな。型システムと関数型プログラミングに興味があるので。</p>
<h2 id="開発補助ツール">開発補助ツール</h2>
<p>Gitクライアントは使わず、コマンドを打ってる。</p>
<p>最近は<a href="https://pnpm.io/" target="_blank" rel="noopener noreferrer">pnpm</a> / <a href="https://mise.jdx.dev/" target="_blank" rel="noopener noreferrer">mise</a>があれば事足りる感じがありますね。時々bunを触ってみるくらい。nodeでtsが動くし、denoを触る機会がない。</p>
<p>あと<a href="https://www.raycast.com/" target="_blank" rel="noopener noreferrer">Raycast</a>、全然使いこなせてないのをなんとかしたい。</p>
<h2 id="物理環境">物理環境</h2>
<p>個人は<code>Macbook M3 Air</code>で、会社はまだ<code>M1 Pro</code>。周りが<code>M3 Pro</code>を使い始めていて羨ましいけど、全然<code>M1 Pro</code>で事足りる。</p>
<p>キーボードは会社が<code>Keychron Q1 Knob</code>を相変わらず、家は<code>HHKB雪</code>。<code>Keyball 44</code>は机の肥やしになってる。</p>
<h2 id="最近の変化">最近の変化</h2>
<p>そんなに変化ない。仕事でCLIのAIツールが使えれば、neovimあたりに乗り換えて遊びたいんだけど使えないので、結局慣れたIDEを使っている。</p>
<p>カスタムするのめんどくさいのでデフォルトで使いたい派は、おとなしく便利なIDEを使えば良いのかもしれない。</p>
<h2 id="まとめ">まとめ</h2>
<p>我ながら面白みがない。もっと生産性を上げるために探索するべきなのか、と思うこともあるけどとにかくめんどくさがりなので、誰かに教えてもらわないとツールが増えない。</p>
<p>miseはいいかも、と思いつつも使い方をすぐ忘れるのでまだ馴染んでない。voltaが楽すぎた節はある。</p>
<p>AI周りは変化が早いので、こういう記録は面白いのかもしれない。来年が楽しみ！</p>
<hr>
<p><em>最終更新: 2026年1月22日</em></p>
]]></content:encoded>
      <category>ツール</category>
      <category>生産性UP</category>
      <category>開発環境</category>
    </item>
    <item>
      <title>TakumiでOG Imageを生成した</title>
      <link>https://tsuka-ryu.dev/posts/2026-01-11-og-image</link>
      <guid isPermaLink="true">https://tsuka-ryu.dev/posts/2026-01-11-og-image</guid>
      <pubDate>Sun, 11 Jan 2026 00:00:00 +0900</pubDate>
      <description>Takumiを使用してブログにOG画像生成機能を追加しました</description>
      <content:encoded><![CDATA[<p>あけましておめでとうございます。年末年始寝込んでいたら、もう1月前半が終わってしまいそうです。まだ病み上がりで元気な感じがないので、早く元気になりたいです。今回はブログにOG画像生成機能を追加したので紹介します。</p>
<h2 id="takumi">Takumi</h2>
<p><a href="https://www.fumadocs.dev/docs/integrations" target="_blank" rel="noopener noreferrer">Fumadocsのドキュメント</a>に出てきたので、今回は<a href="https://takumi.kane.tw/" target="_blank" rel="noopener noreferrer">Takumi</a>というライブラリを使ってOG画像を生成してみました。</p>
<h3 id="og画像生成">OG画像生成</h3>
<p>OG画像生成といえば、vercelのsatoriと、それをベースにした<code>&quot;next/og&quot;</code>の機能だと思います。自分も過去に動的OG画像生成をしたことがあります。</p>
<p>ただ、satoriの<a href="https://github.com/vercel/satori?tab=readme-ov-file#css" target="_blank" rel="noopener noreferrer">CSSのサポート状況</a>を眺めながら実装するのは地味に骨が折れた記憶があります。いまはどうなんでしょう。</p>
<h3 id="takumiの特徴">Takumiの特徴</h3>
<p>特徴は<a href="https://takumi.kane.tw/" target="_blank" rel="noopener noreferrer">公式ドキュメント</a>や<a href="https://github.com/kane50613/takumi" target="_blank" rel="noopener noreferrer">リポジトリ</a>に記載されてるので詳細は割愛しますが、自分が良さそうだなと思ったのは下記です。</p>
<ul>
<li><p>Rustで実装されているため高速（ファイル数が増えるとビルド時間が長くなりがちな場合に有効）</p>
</li>
<li><p>Tailwind CSS ファースト</p>
</li>
</ul>
<h2 id="実装">実装</h2>
<p><a href="https://www.fumadocs.dev/docs/integrations/takumi" target="_blank" rel="noopener noreferrer">Fumadocsのドキュメント</a>通りに実装するだけです。見た目は<a href="https://github.com/kane50613/takumi/tree/master/takumi-template/src/templates" target="_blank" rel="noopener noreferrer">Takumiのtemplate</a>から<a href="https://github.com/tsuka-ryu/tsuka-ryu-s-blog/blob/main/components/og-image/index.tsx" target="_blank" rel="noopener noreferrer">コピペしてちょっと整えました</a>。</p>
<p>fontの読み込みについてもドキュメントに書かれていて、今回はNoto Sans JPを指定してます。ただフォントファイルが9.3MBあるため、サブセット化をしたいと思っています。</p>
<pre><code class="language-tsx">// import文は割愛

export async function GET(_req: Request, { params }: RouteContext&lt;&quot;/og/blog/[...slug]&quot;&gt;) {
  const { slug } = await params;
  const page = blog.getPage(slug.slice(0, -1));
  if (!page) notFound();

  // Load Noto Sans JP font
  const fontPath = path.join(process.cwd(), &quot;public/fonts/NotoSansJP.ttf&quot;);
  const fontData = await fs.readFile(fontPath);

  // Load background image directly from filesystem
  const backgroundImagePath = path.join(process.cwd(), &quot;public/og-background-image.webp&quot;);
  const backgroundImageData = await fs.readFile(backgroundImagePath);
  const backgroundImageBase64 = `data:image/webp;base64,${backgroundImageData.toString(&quot;base64&quot;)}`;

  // Format date for display
  const formattedDate = page.data.date
    ? new Date(page.data.date).toLocaleDateString(&quot;ja-JP&quot;, {
        year: &quot;numeric&quot;,
        month: &quot;long&quot;,
        day: &quot;numeric&quot;,
      })
    : &quot;&quot;;

  return new ImageResponse(
    &lt;BlogPost
      title={page.data.title}
      author={page.data.author}
      date={formattedDate}
      // category={&quot;&quot;} TODO: カテゴリ実装したら追加
      avatar={&quot;https://avatars.githubusercontent.com/u/69495387&quot;}
      backgroundImage={`url(${backgroundImageBase64})`}
    /&gt;,
    {
      width: 1200,
      height: 630,
      format: &quot;webp&quot;,
      fonts: [
        {
          name: &quot;Noto Sans JP&quot;,
          data: fontData,
          weight: 400,
          style: &quot;normal&quot;,
        },
      ],
    },
  );
}
</code></pre>
<h2 id="完成">完成</h2>
<p>実装で特に苦労することもなく、こんな感じになりました。満足です。</p>
<p><img src="https://tsuka-ryu.dev/blog/og-image-example.webp" alt="OG画像のサンプル"></p>
<h2 id="2026-01-17追記">2026/01/17追記</h2>
<p>backgroundImageを使っていて、かつ記事数が4つを超えるとビルドが終わらなくなる現象が発生した。
原因の追究は後回しにして、いったんbackgroundImageを使うのをやめた。
<a href="https://takumi.kane.tw/docs/persistent-images/" target="_blank" rel="noopener noreferrer">Persistent Images</a>を適用しようとしたが失敗した。Next.jsの静的ビルド時に<code>fs.readFile</code>でpublicディレクトリを読み込む際のパス解決に問題があるのかもしれない。
最小構成で再現するか調べてみたい。</p>
<hr>
<p><em>最終更新: 2026年1月17日</em></p>
]]></content:encoded>
      <category>ブログ盆栽</category>
      <category>OGP画像</category>
      <category>Takumi</category>
      <category>rust</category>
    </item>
    <item>
      <title>2025年の振り返り</title>
      <link>https://tsuka-ryu.dev/posts/2025-12-31-recap</link>
      <guid isPermaLink="true">https://tsuka-ryu.dev/posts/2025-12-31-recap</guid>
      <pubDate>Wed, 31 Dec 2025 00:00:00 +0900</pubDate>
      <description>2025年を振り返ります</description>
      <content:encoded><![CDATA[<p>他の方の振り返りを読んでいて楽しくなったので自分も書いてみようと思いました。</p>
<p>今年の総括としては、かなり楽しく過ごせました。けど体調崩しすぎ😔</p>
<h2 id="仕事">仕事</h2>
<p>2025年から役割が変わり、案件のリーダーをやりながら、コードを書くというよりはマネジメントやもう少し上のレイヤーから課題を検知して火消しをして回るような仕事をしていました。自分で必要なことを考えて、必要なだけコミットするという働き方ができて個人的には楽しいです。来年もこの調子でやっていきたいと思います。</p>
<p>フロントエンドのチームを強くするにはどうしたらいいか、といったことをぼんやりと考えていて、JSer.infoを読む会を開催したり、自動テストに関する意識調査などをしたりしていました。とはいえ、自分より技術的に優れた若手がたくさんいるので、実は何もしなくてもいいのではないかと思ったり思わなかったりして日々悩み中です。</p>
<p>たぶん自分に自信がないだけなので、精進してまいりたいですね。</p>
<p>また、仕事で特筆すべきはセキュリティ周りでしょうか。Next.jsのmiddlewareに関する脆弱性や、年末にはRSCの特大の脆弱性が発生して社内でバタバタしていました。サプライチェーンアタックやフィッシングなど、今年はかなりセキュリティを意識するタイミングが多く、普段からアップデートできる環境を整えたいと思いつつ、案件によって経緯があるため難しいなと感じていました。来年の課題ですね。</p>
<h2 id="カンファレンス">カンファレンス</h2>
<p>今年はカンファレンスに参加してみようと思っていて、それを実現できた年でした。オフラインで参加したのは以下のイベントです。普段インターネットで見る凄腕エンジニアたちを間近で見られて、かつ懇親会などではお話もさせていただき、とても楽しかったです。みんな優しくて良い人ばかり。</p>
<ul>
<li><p>フロントエンドチョットデキル 2025</p>
</li>
<li><p>TSKaigi 2025</p>
</li>
<li><p>Vue Fes Japan 2025</p>
</li>
<li><p>TSKaigi Hokuriku 2025</p>
</li>
</ul>
<h3 id="tskaigi-hokuriku-2025">TSKaigi Hokuriku 2025</h3>
<p>TSKaigi Hokuriku 2025では、<a href="https://hokuriku.tskaigi.org/talks/37" target="_blank" rel="noopener noreferrer">「<code>tsc --init</code> の設計思想の変化とその背景を追う - &quot;教育的&quot;アプローチから実用性重視への転換」</a>で初めて登壇させていただきました。とても緊張しましたが、会社のメンバー大勢で行っていたのでなんとかなった気がします。</p>
<p>uhyoさんの基調講演で恐れ多くも言及いただいたのですが、すみません、そのプロパティについては触れていないです...と震えていました（笑）。</p>
<p>また、カンファレンス終わりにインフルに罹ってしまい、40℃の熱が出たのもいい思い出です（？）</p>
<h2 id="zennスクラップ">Zennスクラップ</h2>
<p>ここでOSSコントリビュートについて書けると良いのですが、まだそういったことはしたことがないので、来年は頑張りたいです。代わりに、興味を持った記事や技術のドキュメントを眺めては<a href="https://zenn.dev/sushin_ya?tab=scraps" target="_blank" rel="noopener noreferrer">Zennスクラップ</a>に雑にメモをするということをやっていました。</p>
<p>Zenn 2025 Recapは以下の感じだったそうです。</p>
<ul>
<li><p>487,694字の執筆</p>
</li>
<li><p>89のスクラップ</p>
</li>
<li><p>0ページレビュー</p>
</li>
<li><p>43 Likes</p>
</li>
</ul>
<p>正直、ドキュメントを眺めるだけでは意味がないなと思ったので来年はあまりやらないと思います。ライブラリなら実際に使ってみたり、コードを読み書きしないと記憶に何も残らないなと感じたので...。</p>
<p>内容は覚えていないのですが、なんとなく面白かった気がするものを列挙してみます。</p>
<h3 id="ecmascript-の仕様を読むためのブログを読む">ECMAScript の仕様を読むためのブログを読む</h3>
<ul>
<li><p>V8のブログシリーズで、ECMAの仕様書を読むための基礎知識が解説されているものを読みました</p>
</li>
<li><p>カバー文法などJSの奥深さがわかって面白かった気がしますが、忘れてしまいました</p>
</li>
</ul>
<h3 id="ウェブアクセシビリティ基盤委員会の資料を眺める">ウェブアクセシビリティ基盤委員会の資料を眺める</h3>
<ul>
<li><p>WAICのサイトや関連資料を一通り眺めました</p>
</li>
<li><p>何をしたら良いかはふんわりわかりましたが、WCAG 2.0は読んでいないので来年頑張りたいですね</p>
</li>
<li><p>最近アクセシビリティが要件に含まれることが多くなってきたので、かなり切実です</p>
</li>
</ul>
<h3 id="json-parsing-from-scratch-in-haskell-を読む">JSON Parsing from Scratch in Haskell を読む</h3>
<ul>
<li><p>HaskellでJSONパーサーを書くチュートリアルです</p>
</li>
<li><p>結構短くて良い内容でしたが、ApplicativeやAlternativeやMonadが出てきました</p>
</li>
<li><p>その後、LLMの力を借りてTypeScriptで再実装したりしました</p>
</li>
</ul>
<h3 id="quiet-uiを学ぶ">Quiet UIを学ぶ</h3>
<ul>
<li><p>カスタム要素で構築されたUIライブラリ</p>
</li>
<li><p>リッチなUIも多く、実装を読んで勉強になりました</p>
</li>
<li><p>残念ながら、現在は公開されていないようです</p>
</li>
</ul>
<h3 id="css-wrapped-2025を眺める">CSS Wrapped 2025を眺める</h3>
<ul>
<li><p>CSSの新機能をまとめたものを眺めました</p>
</li>
<li><p>Interopが始まってからどんどん新しい機能が増えていて、楽しいですね</p>
</li>
<li><p>実際の案件で使う前に自分で触ってみないと発想として出てこないので、来年はそこら辺を素振りしていきたいですね</p>
</li>
</ul>
<h3 id="最近興味があること">最近興味があること</h3>
<p>React/Next.jsの動向は追っていますが、どちらかというとアクセシビリティやHTML/CSSの最新動向が気になってきています。JavaScriptを使わずにできることが増えるのは良いことですね。実装の際にパッと思いつかないことが多いので、素振りをして自分の引き出しに入れておきたいと思いました。</p>
<p>oxlintやoxfmtも気になっているので来年は何かできたら良いなと思っています。そういえばECMAの仕様を読むためのブログは、Oxcのドキュメントで見つけて読もうと思っていたのですが忘れていました。</p>
<h2 id="人生">人生</h2>
<p>今年はやたらと体調を崩した年でした。手元のメモで数えてみたら40日以上は体調不良だったようです。去年から13kg減量に成功したので、それの影響もあるのかと思いつつ、たぶんお酒のせいです。お酒を飲むと風邪をひきます。免疫力を上げていきたい今日この頃です。</p>
<p>年始の1月はほぼ体調が悪く、その後は8月末のアメリカ旅行中〜旅行後のひどい風邪と、11月末のインフルがきつかったですね。</p>
<p>実は今も体調を崩してしまって、帰省の予定をキャンセルして引きこもっています。来年は本当になんとかしたいですね...</p>
<p>また、ライフステージを進めるために課金したりしているので、来年はそこら辺で成果が出ると嬉しいのですが、将来のことを思うとモヤモヤと思考が巡ってしまうのでした。明日はどっちだ。</p>
<h2 id="まとめ">まとめ</h2>
<p>日々楽しく生活できているのですが、もっとコンフォートゾーンを出てOSSに挑戦してみたり、体調を安定させて旅行に行っても元気でいられる体を作りたいなと思っているところです。自分は何かに挑戦するのが苦手で、引きこもってぼーっとYouTubeを見たり本を読んだりしてしまうので、そこから抜け出すように2026年は頑張っていきたいです。</p>
<p>もう2026年になってしまうので、ここら辺で切り上げます。</p>
<p>良いお年を！</p>
<hr>
<p><em>最終更新: 2025年12月31日</em></p>
]]></content:encoded>
      <category>recap</category>
    </item>
    <item>
      <title>個人ブログを作ったよ</title>
      <link>https://tsuka-ryu.dev/posts/2025-12-30-my-blog</link>
      <guid isPermaLink="true">https://tsuka-ryu.dev/posts/2025-12-30-my-blog</guid>
      <pubDate>Tue, 30 Dec 2025 00:00:00 +0900</pubDate>
      <description>tsuka-ryuの個人ブログを作成したのでメモ</description>
      <content:encoded><![CDATA[<h2 id="ご挨拶">ご挨拶</h2>
<p>こんにちは、tsuka-ryuと申します。2025年も残すところあと2日となりました。早いものですね。今年はどんな一年だったでしょうか。</p>
<p>シンプルではありますが、この度、個人ブログを作ってみました。これまでも何度も作ろうと試みてきたのですが、追加したい機能に対して手が追いつかず、Vibe Codingで勢いよく作っても、レビューをしないでマージすると途中で破綻したり、満足いくものが出来ませんでした。</p>
<p>ですが、今回は欲しかった機能は一揃いあるし、かっちょいいシェーダーライブラリも導入してそれっぽくなったので満足してます。今後ぼちぼちブログを書いたり、このブログを盆栽代わりにしていけたら良いなと思っています。</p>
<p>どうぞよろしくお願い致します。</p>
<h2 id="fumadocsとpaper-shaders">FumadocsとPaper shaders</h2>
<h3 id="fumadocs">Fumadocs</h3>
<p>このサイトは<a href="https://www.fumadocs.dev/" target="_blank" rel="noopener noreferrer">Fumadocs</a>というドキュメンテーションフレームワークをベースにNext.jsで作成しています。作者の<a href="https://x.com/fuma_nama" target="_blank" rel="noopener noreferrer">Fuma Nama</a>氏による美しいデザイン、複数のフレームワークを選択できる柔軟さ、カスタマイズ性の高さ、簡単に追加できる機能の豊富さが特徴です。</p>
<p>あと<a href="https://www.fuma-nama.dev/blog/svg-art" target="_blank" rel="noopener noreferrer">Clerk TOC</a>もかっこよくて、つい見出しを増やしたくなりますね。</p>
<p>ドキュメントもしっかりしているので車輪の再発明を回避しつつ、やりたいことをすぐに実現できました。このブログ自体もデザインがない状態から、2、3日ほどで作成しました。本当にありがたいことですね。</p>
<h3 id="paper-shaders">Paper shaders</h3>
<p><a href="https://shaders.paper.design/" target="_blank" rel="noopener noreferrer">Paper shaders</a>はFumadocs公式のHero部分で使われていたもので、<em>ultra fast zero-dependency shaders for your designs</em> らしいです。最高ですね。</p>
<p>サイト上で数値をいじいじして、生成されたコンポーネントを貼り付けて最終調整をしたらいい感じになってかなり気に入っています。</p>
<h3 id="ホスティング-インフラ">ホスティング・インフラ</h3>
<p>ホスティングは楽なので一旦Vercelにしていますが、そのうちAWSに載せ替えて、ドメインも取得したいところです。</p>
<h2 id="取り上げたいトピック">取り上げたいトピック</h2>
<p>個人ブログなので好きなことについてひっそり書きたいです。おおまかに以下のようなトピックが書けたらと思います。</p>
<h3 id="フロントエンドエコシステム関連">フロントエンドエコシステム関連</h3>
<ul>
<li><p>React/Next.js/TypeScriptを中心に、今年注目されたVoid Zero周り、linterやformatterなど</p>
</li>
<li><p>おもしろいライブラリがあったら遊ぶとか</p>
</li>
<li><p>HTML/CSS/JSのWeb標準周りの動向</p>
</li>
<li><p>記事やpodcastの感想もちらしの裏的に書けたら良いかも</p>
</li>
</ul>
<h3 id="マネージメント-組織論">マネージメント、組織論</h3>
<ul>
<li><p>効果的にマネジメントしたいという気持ちなので、その辺りの雑感</p>
</li>
<li><p>どうやって良い組織を作っていくか</p>
</li>
<li><p>読んだ本の感想</p>
</li>
</ul>
<h3 id="その他">その他</h3>
<ul>
<li><p>FigmaやLLM関連</p>
</li>
<li><p>アルゴリズムとデータ構造を勉強したいのでそこらへん</p>
</li>
<li><p>HaskellやRustにも挑戦したい（願望）</p>
</li>
</ul>
<h2 id="今後の抱負">今後の抱負</h2>
<p>2024年頃までは業務でコードを書く機会がそれなりにあったのですが、今年に入ってからはあまりコードを書かなくなってきたので若干危機感がありました。2026年もおそらく似たような状態になることが予想されます。</p>
<p>いくつかのカンファレンスに初めて参加したり、登壇させていただいたり、刺激はかなりもらえたんですけどね。</p>
<p>一方で、自分の技術力はまだまだ足りないと感じることが多く、もっとコードを書いてたくさんのことを知りたいと思っています。なので、今後はこのサイトをいじったり、記事を書いたりしてアウトプットする機会を増やしていきたいと考えています。</p>
<p>月一くらいで記事が書けたらいいなあ、くらいな感じでしょうか。
三日坊主がちな人間なのですぐに諦めるかもしれませんが、半年に一回でもいいから書きたいなと思っています。</p>
<p>普段RSSでたくさんの方の個人ブログを読んでいて、なんだか羨ましく感じていたので、自分も似たようなことをしたいだけなんですけどね。</p>
<p>それでは、良いお年を。</p>
<hr>
<p><em>最終更新: 2025年12月30日</em></p>
]]></content:encoded>
      <category>ブログ盆栽</category>
      <category>Next.js</category>
    </item>
  </channel>
</rss>
